Repository: iiYii/getyii Branch: master Commit: f6c3f7f241cd Files: 409 Total size: 555.5 KB Directory structure: gitextract_1v5jxhfe/ ├── .bowerrc ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE.md ├── README.md ├── backend/ │ ├── assets/ │ │ └── AppAsset.php │ ├── config/ │ │ ├── .gitignore │ │ ├── bootstrap.php │ │ ├── main.php │ │ └── params.php │ ├── controllers/ │ │ ├── CenterController.php │ │ ├── Controller.php │ │ ├── NavController.php │ │ ├── NavUrlController.php │ │ ├── PostController.php │ │ ├── PostMetaController.php │ │ ├── RightLinkController.php │ │ ├── SearchLogController.php │ │ ├── SiteController.php │ │ └── UserController.php │ ├── models/ │ │ ├── .gitkeep │ │ ├── PostSearch.php │ │ ├── RightLinkSearch.php │ │ ├── SearchLogSearch.php │ │ └── User.php │ ├── runtime/ │ │ └── .gitignore │ ├── views/ │ │ ├── layouts/ │ │ │ ├── content.php │ │ │ ├── header.php │ │ │ ├── left.php │ │ │ ├── main-login.php │ │ │ └── main.php │ │ ├── nav/ │ │ │ ├── _form.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ │ ├── nav-url/ │ │ │ ├── _form.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ │ ├── post/ │ │ │ ├── _form.php │ │ │ ├── _search.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ │ ├── post-meta/ │ │ │ ├── _form.php │ │ │ ├── _search.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ │ ├── right-link/ │ │ │ ├── _form.php │ │ │ ├── _search.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ │ ├── search-log/ │ │ │ ├── _search.php │ │ │ └── index.php │ │ ├── site/ │ │ │ ├── error.php │ │ │ ├── index.php │ │ │ └── login.php │ │ └── user/ │ │ ├── _form.php │ │ ├── _search.php │ │ ├── index.php │ │ ├── update.php │ │ └── view.php │ └── web/ │ ├── .gitignore │ ├── .htaccess │ ├── assets/ │ │ └── .gitignore │ ├── css/ │ │ ├── sb-admin-2.css │ │ └── site.css │ ├── js/ │ │ └── sb-admin-2.js │ └── robots.txt ├── common/ │ ├── assets/ │ │ ├── AtJs.php │ │ ├── CaretJs.php │ │ └── DropzoneJs.php │ ├── components/ │ │ ├── AssetBundle.php │ │ ├── ComposerInstaller.php │ │ ├── Config.php │ │ ├── Controller.php │ │ ├── DbAuthManager.php │ │ ├── FileTarget.php │ │ ├── GlobalFunctions.php │ │ ├── Mailer.php │ │ └── db/ │ │ ├── ActiveRecord.php │ │ ├── Command.php │ │ ├── Connection.php │ │ └── Migration.php │ ├── config/ │ │ ├── .gitignore │ │ ├── bootstrap.php │ │ ├── main.php │ │ └── params.php │ ├── grid/ │ │ └── EnumColumn.php │ ├── helpers/ │ │ ├── Arr.php │ │ ├── Avatar.php │ │ ├── Formatter.php │ │ └── UploadHelper.php │ ├── mail/ │ │ ├── backup.php │ │ ├── layouts/ │ │ │ └── html.php │ │ └── passwordResetToken.php │ ├── messages/ │ │ ├── pt-BR/ │ │ │ ├── backend.php │ │ │ ├── common.php │ │ │ └── frontend.php │ │ └── zh-CN/ │ │ ├── backend.php │ │ ├── common.php │ │ └── frontend.php │ ├── models/ │ │ ├── LoginForm.php │ │ ├── Nav.php │ │ ├── NavUrl.php │ │ ├── Post.php │ │ ├── PostComment.php │ │ ├── PostMeta.php │ │ ├── PostMetaSearch.php │ │ ├── PostSearch.php │ │ ├── PostTag.php │ │ ├── PostTagSearch.php │ │ ├── RightLink.php │ │ ├── Search.php │ │ ├── SearchLog.php │ │ ├── Session.php │ │ ├── User.php │ │ ├── UserInfo.php │ │ └── UserSearch.php │ ├── services/ │ │ ├── CommentService.php │ │ ├── NotificationService.php │ │ ├── PostService.php │ │ ├── TopicService.php │ │ ├── TweetService.php │ │ └── UserService.php │ └── widgets/ │ └── JsBlock.php ├── composer.json ├── console/ │ ├── config/ │ │ ├── .gitignore │ │ ├── bootstrap.php │ │ ├── main.php │ │ └── params.php │ ├── controllers/ │ │ ├── .gitkeep │ │ ├── InstallController.php │ │ └── SyncController.php │ ├── migrations/ │ │ ├── m130524_201442_init.php │ │ ├── m150104_071047_init_blog.php │ │ ├── m150104_091352_init_user.php │ │ ├── m150115_081356_create_user_info.php │ │ ├── m150201_142415_update_user.php │ │ ├── m150205_085033_update_post_comment.php │ │ ├── m150209_015931_setting_init.php │ │ ├── m150209_090406_create_user_account.php │ │ ├── m150211_070335_update_user_info.php │ │ ├── m150212_030127_update_user_info_and_post_meta.php │ │ ├── m150212_132400_create_topics_table.php │ │ ├── m150214_070754_update_post_meta.php │ │ ├── m150412_034147_update_site_setting.php │ │ ├── m150416_134819_create_notification_table.php │ │ ├── m150420_060807_update_post_table.php │ │ ├── m150424_025409_update_table_engine.php │ │ ├── m150424_031429_update_notification_table.php │ │ ├── m150424_100155_update_post_meta_table.php │ │ ├── m150425_031844_create_right_link.php │ │ ├── m150626_073539_create_nav.php │ │ ├── m150626_073559_create_nav_url.php │ │ ├── m150702_130239_create_session_init.php │ │ ├── m150805_085832_create_search_log_table.php │ │ ├── m150808_025734_update_table_character.php │ │ ├── m150829_091943_update_post_table.php │ │ ├── m160320_093621_create_merit_table.php │ │ ├── m160321_132724_add_donate_table.php │ │ ├── m160719_093527_modify_user.php │ │ ├── m190624_022722_create_spam_table.php │ │ ├── m190908_053628_init_admin.php │ │ └── m190908_055507_init_data.php │ ├── models/ │ │ └── .gitkeep │ └── runtime/ │ └── .gitignore ├── doc/ │ └── README.md ├── docker-files/ │ ├── docker-compose-example.yml │ ├── getyii.com.conf │ └── run.sh ├── environments/ │ ├── dev/ │ │ ├── backend/ │ │ │ ├── config/ │ │ │ │ ├── main-local.php │ │ │ │ └── params-local.php │ │ │ └── web/ │ │ │ ├── index-test.php │ │ │ └── index.php │ │ ├── common/ │ │ │ └── config/ │ │ │ ├── db.php │ │ │ ├── main-local.php │ │ │ └── params-local.php │ │ ├── console/ │ │ │ └── config/ │ │ │ ├── main-local.php │ │ │ └── params-local.php │ │ ├── frontend/ │ │ │ ├── config/ │ │ │ │ ├── main-local.php │ │ │ │ └── params-local.php │ │ │ └── web/ │ │ │ ├── index-test.php │ │ │ └── index.php │ │ └── yii │ ├── index.php │ └── prod/ │ ├── backend/ │ │ ├── config/ │ │ │ ├── main-local.php │ │ │ └── params-local.php │ │ └── web/ │ │ └── index.php │ ├── common/ │ │ └── config/ │ │ ├── db.php │ │ ├── main-local.php │ │ └── params-local.php │ ├── console/ │ │ └── config/ │ │ ├── main-local.php │ │ └── params-local.php │ ├── frontend/ │ │ ├── config/ │ │ │ ├── main-local.php │ │ │ └── params-local.php │ │ └── web/ │ │ └── index.php │ └── yii ├── frontend/ │ ├── assets/ │ │ ├── AppAsset.php │ │ ├── AtJsAsset.php │ │ ├── BowerAsset.php │ │ ├── EditorAsset.php │ │ └── EmojifyAsset.php │ ├── behaviors/ │ │ └── AfterLoginBehavior.php │ ├── config/ │ │ ├── .gitignore │ │ ├── bootstrap.php │ │ ├── main.php │ │ ├── params.php │ │ └── search.ini │ ├── controllers/ │ │ ├── NotificationController.php │ │ ├── PostTagController.php │ │ └── SiteController.php │ ├── messages/ │ │ └── zh-CN/ │ │ └── app.php │ ├── models/ │ │ ├── ContactForm.php │ │ ├── Notification.php │ │ ├── PasswordResetRequestForm.php │ │ ├── ResetPasswordForm.php │ │ └── SignupForm.php │ ├── modules/ │ │ ├── nav/ │ │ │ ├── Module.php │ │ │ ├── controllers/ │ │ │ │ └── DefaultController.php │ │ │ └── views/ │ │ │ └── default/ │ │ │ └── index.php │ │ ├── topic/ │ │ │ ├── Module.php │ │ │ ├── controllers/ │ │ │ │ ├── CommentController.php │ │ │ │ └── DefaultController.php │ │ │ ├── models/ │ │ │ │ └── Topic.php │ │ │ └── views/ │ │ │ ├── comment/ │ │ │ │ ├── _form.php │ │ │ │ ├── _item.php │ │ │ │ ├── create.php │ │ │ │ ├── index.php │ │ │ │ └── update.php │ │ │ └── default/ │ │ │ ├── _form.php │ │ │ ├── _item.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ │ ├── tweet/ │ │ │ ├── Module.php │ │ │ ├── controllers/ │ │ │ │ └── DefaultController.php │ │ │ ├── models/ │ │ │ │ ├── Tweet.php │ │ │ │ └── TweetSearch.php │ │ │ └── views/ │ │ │ └── default/ │ │ │ ├── _form.php │ │ │ ├── _item.php │ │ │ └── index.php │ │ └── user/ │ │ ├── Module.php │ │ ├── controllers/ │ │ │ ├── ActionController.php │ │ │ ├── DefaultController.php │ │ │ ├── SecurityController.php │ │ │ └── SettingController.php │ │ ├── models/ │ │ │ ├── AccountForm.php │ │ │ ├── AvatarForm.php │ │ │ ├── Donate.php │ │ │ ├── UserAccount.php │ │ │ └── UserMeta.php │ │ └── views/ │ │ ├── default/ │ │ │ ├── _view.php │ │ │ └── show.php │ │ └── setting/ │ │ ├── _alert.php │ │ ├── _menu.php │ │ ├── account.php │ │ ├── avatar.php │ │ ├── donate.php │ │ ├── networks.php │ │ └── profile.php │ ├── runtime/ │ │ └── .gitignore │ ├── views/ │ │ ├── layouts/ │ │ │ └── main.php │ │ ├── notification/ │ │ │ ├── _item.php │ │ │ └── index.php │ │ ├── partials/ │ │ │ ├── markdwon_help.php │ │ │ └── users.php │ │ └── site/ │ │ ├── _item.php │ │ ├── about.php │ │ ├── contact.php │ │ ├── contributors.php │ │ ├── error.php │ │ ├── getstart.php │ │ ├── index.php │ │ ├── login.php │ │ ├── markdown.php │ │ ├── requestPasswordResetToken.php │ │ ├── resetPassword.php │ │ ├── signup.php │ │ ├── tags.php │ │ ├── timeline.php │ │ └── users.php │ ├── web/ │ │ ├── .gitignore │ │ ├── .htaccess │ │ ├── assets/ │ │ │ └── .gitignore │ │ ├── css/ │ │ │ ├── global.css │ │ │ ├── site-ruyi.css │ │ │ └── site.css │ │ ├── js/ │ │ │ ├── At.js │ │ │ ├── editor.js │ │ │ ├── jquery.pin.js │ │ │ ├── main.js │ │ │ ├── nav.js │ │ │ └── topic.js │ │ ├── robots.txt │ │ └── uploads/ │ │ └── .gitignore │ └── widgets/ │ ├── Alert.php │ ├── Connect.php │ ├── Nav.php │ ├── NewestPost.php │ ├── Node.php │ ├── Panel.php │ ├── TopicSidebar.php │ └── views/ │ ├── nav.php │ ├── node.php │ ├── panel.php │ └── topicSidebar.php ├── init ├── init.bat ├── requirements.php ├── tests/ │ ├── README.md │ ├── codeception/ │ │ ├── _output/ │ │ │ └── .gitignore │ │ ├── backend/ │ │ │ ├── .gitignore │ │ │ ├── _bootstrap.php │ │ │ ├── _output/ │ │ │ │ └── .gitignore │ │ │ ├── acceptance/ │ │ │ │ ├── LoginCept.php │ │ │ │ └── _bootstrap.php │ │ │ ├── acceptance.suite.yml │ │ │ ├── codeception.yml │ │ │ ├── functional/ │ │ │ │ ├── LoginCept.php │ │ │ │ └── _bootstrap.php │ │ │ ├── functional.suite.yml │ │ │ ├── unit/ │ │ │ │ ├── DbTestCase.php │ │ │ │ ├── TestCase.php │ │ │ │ ├── _bootstrap.php │ │ │ │ └── fixtures/ │ │ │ │ └── data/ │ │ │ │ └── .gitkeep │ │ │ └── unit.suite.yml │ │ ├── bin/ │ │ │ ├── _bootstrap.php │ │ │ ├── yii │ │ │ └── yii.bat │ │ ├── common/ │ │ │ ├── .gitignore │ │ │ ├── _bootstrap.php │ │ │ ├── _output/ │ │ │ │ └── .gitignore │ │ │ ├── _pages/ │ │ │ │ └── LoginPage.php │ │ │ ├── _support/ │ │ │ │ └── FixtureHelper.php │ │ │ ├── codeception.yml │ │ │ ├── fixtures/ │ │ │ │ ├── UserFixture.php │ │ │ │ └── data/ │ │ │ │ └── init_login.php │ │ │ ├── templates/ │ │ │ │ └── fixtures/ │ │ │ │ └── user.php │ │ │ ├── unit/ │ │ │ │ ├── DbTestCase.php │ │ │ │ ├── TestCase.php │ │ │ │ ├── _bootstrap.php │ │ │ │ ├── fixtures/ │ │ │ │ │ └── data/ │ │ │ │ │ └── models/ │ │ │ │ │ └── user.php │ │ │ │ └── models/ │ │ │ │ └── LoginFormTest.php │ │ │ └── unit.suite.yml │ │ ├── config/ │ │ │ ├── acceptance.php │ │ │ ├── backend/ │ │ │ │ ├── acceptance.php │ │ │ │ ├── config.php │ │ │ │ ├── functional.php │ │ │ │ └── unit.php │ │ │ ├── common/ │ │ │ │ └── unit.php │ │ │ ├── config.php │ │ │ ├── console/ │ │ │ │ └── unit.php │ │ │ ├── frontend/ │ │ │ │ ├── acceptance.php │ │ │ │ ├── config.php │ │ │ │ ├── functional.php │ │ │ │ └── unit.php │ │ │ ├── functional.php │ │ │ └── unit.php │ │ ├── console/ │ │ │ ├── .gitignore │ │ │ ├── _bootstrap.php │ │ │ ├── _output/ │ │ │ │ └── .gitignore │ │ │ ├── codeception.yml │ │ │ ├── unit/ │ │ │ │ ├── DbTestCase.php │ │ │ │ ├── TestCase.php │ │ │ │ ├── _bootstrap.php │ │ │ │ └── fixtures/ │ │ │ │ └── data/ │ │ │ │ └── .gitkeep │ │ │ └── unit.suite.yml │ │ └── frontend/ │ │ ├── .gitignore │ │ ├── _bootstrap.php │ │ ├── _output/ │ │ │ └── .gitignore │ │ ├── _pages/ │ │ │ ├── AboutPage.php │ │ │ ├── ContactPage.php │ │ │ └── SignupPage.php │ │ ├── acceptance/ │ │ │ ├── AboutCept.php │ │ │ ├── ContactCept.php │ │ │ ├── HomeCept.php │ │ │ ├── LoginCept.php │ │ │ ├── SignupCest.php │ │ │ └── _bootstrap.php │ │ ├── acceptance.suite.yml │ │ ├── codeception.yml │ │ ├── functional/ │ │ │ ├── AboutCept.php │ │ │ ├── ContactCept.php │ │ │ ├── HomeCept.php │ │ │ ├── LoginCept.php │ │ │ ├── SignupCest.php │ │ │ └── _bootstrap.php │ │ ├── functional.suite.yml │ │ ├── unit/ │ │ │ ├── DbTestCase.php │ │ │ ├── TestCase.php │ │ │ ├── _bootstrap.php │ │ │ ├── fixtures/ │ │ │ │ └── data/ │ │ │ │ └── models/ │ │ │ │ └── user.php │ │ │ └── models/ │ │ │ ├── ContactFormTest.php │ │ │ ├── PasswordResetRequestFormTest.php │ │ │ ├── ResetPasswordFormTest.php │ │ │ └── SignupFormTest.php │ │ └── unit.suite.yml │ └── codeception.yml └── yii.bat ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bowerrc ================================================ { "directory" : "vendor/bower" } ================================================ FILE: .gitignore ================================================ # yii console command /yii # phpstorm 项目文件 .idea # netbeans 项目文件 nbproject # zend studio for eclipse 项目文件 .buildpath .project .settings # windows 缩略图换成 Thumbs.db # composer 供应商目录 /vendor # composer 它本身是不需要的(客户端自行升级最新版) composer.phar # Mac DS_Store 文件 .DS_Store # phpunit 它本身是不需要的 phpunit.phar # 本地 phpunit 配置文件 /phpunit.xml /docker-compose.yml /supervisord.log /supervisord.pid # database config /common/config/db-local.php # install lock file install.lock ================================================ FILE: .travis.yml ================================================ language: php php: - 5.4 - 5.5 - 5.6 install: - travis_retry composer self-update && composer --version - travis_retry composer global require "fxp/composer-asset-plugin:~1.1.1" - export PATH="$HOME/.composer/vendor/bin:$PATH" before_script: composer install --prefer-source #script: phpunit --configuration phpunit.xml.dist cache: directories: - vendor #matrix: # allow_failures: # - php: hhvm ================================================ FILE: Dockerfile ================================================ FROM dcb9/php-fpm:latest MAINTAINER Bob RUN apt-get update \ && apt-get install -y --no-install-recommends git vim \ && rm -rf /var/lib/apt/lists/* # http://serverfault.com/questions/599103/make-a-docker-application-write-to-stdout RUN ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log \ && mkdir /app WORKDIR /app ENV COMPOSER_HOME /root/.composer ENV PATH /root/.composer/vendor/bin:$PATH RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ # add chinese image http://pkg.phpcomposer.com/ && composer config -g repositories.packagist composer http://packagist.phpcomposer.com \ && /usr/local/bin/composer global require --prefer-source --no-interaction "fxp/composer-asset-plugin" COPY docker-files/getyii.com.conf /etc/nginx/conf.d/ RUN docker-php-ext-install mysqli pdo pdo_mysql \ && rm -rf /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/example_ssl.conf COPY . /app/ RUN chmod 700 docker-files/run.sh init VOLUME ["/root/.composer", "/app/vendor"] CMD ["docker-files/run.sh"] EXPOSE 80 ================================================ FILE: LICENSE.md ================================================ The Yii framework is free software. It is released under the terms of the following BSD License. Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Yii Software LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ GetYii ================== [![Latest Stable Version](https://poser.pugx.org/iiyii/getyii/v/stable)](https://packagist.org/packages/iiyii/getyii) [![Total Downloads](https://poser.pugx.org/iiyii/getyii/downloads)](https://packagist.org/packages/iiyii/getyii) [![Latest Unstable Version](https://poser.pugx.org/iiyii/getyii/v/unstable)](https://packagist.org/packages/iiyii/getyii) [![License](https://poser.pugx.org/iiyii/getyii/license)](https://packagist.org/packages/iiyii/getyii) community for Yii2 ## 说明 你现在看到的是全新版本的 GetYii 之前的版本我放在 V1 分支上面了,那个版本以后可能就不会更新了。 「doc/images」文件夹里面有截图,你们可以看一下。 全新的 GetYii 只专注于社区,现在基本功能已经 OK 了,以后我们会不断完善的。分享我们的 [trello 项目管理地址](https://trello.com/b/rsZAtG1Y/getyii)。 ## 项目搭建 ### 原始安装方法(推荐) 1、首先你要安装 [Composer](http://www.yiiframework.com/doc-2.0/guide-start-installation.html#installing-via-composer),然后你需要手动去新建一个数据库,比方说新建 `getyii` 数据库,如果想使用 emoji 表情的话,意见使用 `utf8mb4` 编码格式,不想用的话, 建议使用 `utf8` 编码格式。 ``` git clone https://github.com/iiYii/getyii.git cd getyii composer install php init ``` 2、然后修改数据库配置文件的配置信息 ``` vim common/config/db.php ``` 再使用运行我写的安装程序(帮你生成数据库表和假数据) ``` php yii install ``` 或者直接执行数据库迁移工具生成数据库表 ``` php yii migrate ``` ### composer 安装方法(可能不是最新的) 1、首先你要安装 [Composer](http://www.yiiframework.com/doc-2.0/guide-start-installation.html#installing-via-composer),然后你需要手动去新建一个数据库,比方说新建 `getyii` 数据库,如果想使用 emoji 表情的话,意见使用 `utf8mb4` 编码格式,不想用的话, 建议使用 `utf8` 编码格式。 ``` composer create-project --prefer-dist --stability=dev iiyii/getyii getyii cd getyii php init ``` 2、然后复制一份数据库配置,并且修改数据库配置, ``` cp common/config/db.php common/config/db-local.php ``` 再使用运行我写的安装程序(帮你生成数据库表和假数据) ``` php yii install ``` 或者直接执行数据库迁移工具生成数据库表 ``` php yii migrate ``` ### docker 搭建方法 1. 安装好 docker 保证可以运行 docker 和 docker-compose 命令 2. 克隆代码到你本地,并 cd 到相应目录 3. 启动 getyii 应用 $ cp docker-files/docker-compose-example.yml docker-compose.yml $ docker-compose up -d 访问 getyii 添加以下两个域名加到自己机器的 host 里面 .dev.getyii.com 前台 .dev.admin.getyii.com 后台 ### 用户相关 1. 把 user 表中的某用户值 role 字段值改为20,即为前台管理员,目前可以给帖子加精华,不能登录后台。 1. 把 user 表中的某用户值 role 字段值改为30,即为超级管理员,可登录后台。 ## 文档和手册 1. [Yii2手册](http://book.getyii.com) 2. [中文 Composer 手册](http://docs.phpcomposer.com/) ## 安装遇到问题怎么办? 建议在官网的[社区版块](http://www.getyii.com/topic/default/create)**新手提问**下面提问,我会抽空亲自回答。请最大可能的把问题描述清楚,以免浪费我们彼此的时间。 ## 交流群 - Yii2 中国交流群:343188481 - Get√Yii 开发者群:321493381 ## 捐赠 ![微信支付](https://raw.githubusercontent.com/iiYii/getyii/master/wechat-pay.png) ![支付宝支付](https://raw.githubusercontent.com/iiYii/getyii/master/ali-pay.png) 手机微信或者支付宝扫描上方二维码可向本项目捐款 感谢以下这些朋友的资金支持,所得捐赠将用于改善网站服务器、购买开发/调试设备&工具。 捐赠人 | 金额 | 时间 | 说明 -------|------|------ | ------ 张** | 1.00 | 2015年7月7日 | http://asjmtz.com/ *作军 | 100.00 | 2015年08月07日 | dba-china 树* | 333.00 | 2015年09月11日 | http://www.21cnjy.com/ *作军 | 300.00 | 2016年04月28日 | dba-china *勇 | 20.00 | 2017年05月31日 | http://www.fecshop.com/ *勇 | 66.00 | 2017年12月21日 | http://www.fecshop.com/ ## 他们正在使用 GetYii - DBA-CHINA - [Fecshop 社区](http://www.fecshop.com/) ## 感谢 - 感谢 [Ruby-China](https://github.com/ruby-china/ruby-china) 的开源代码。 - 感谢 [PHPHub](https://github.com/summerblue/phphub) 的开源代码。 - 感谢 [huajuan](https://github.com/callmez/huajuan) 的开源代码。 - 最后再感谢一下女朋友的支持 <(▰˘◡˘▰)>。 PS: 如果你暂时无法使用 `composer` 的话,访问链接: 密码: ux6c 下载 zip 文件解压就可以用了。然后你要做的是: - 新建数据库导入 getyii-2015-11-3.sql 数据库 - 修改 `common\config\db-local.php` 文件的数据库配置 - 默认用户名是`admin`,密码是`123456` ================================================ FILE: backend/assets/AppAsset.php ================================================ * @since 2.0 */ class AppAsset extends AssetBundle { public $basePath = '@webroot'; public $baseUrl = '@web'; public $css = [ 'css/site.css', ]; public $js = [ ]; public $depends = [ 'yii\web\YiiAsset', 'yii\bootstrap\BootstrapAsset', ]; } ================================================ FILE: backend/config/.gitignore ================================================ main-local.php params-local.php ================================================ FILE: backend/config/bootstrap.php ================================================ 'app-backend', 'basePath' => dirname(__DIR__), 'controllerNamespace' => 'backend\controllers', 'bootstrap' => ['log'], 'language' => 'zh-CN', 'modules' => [ 'setting' => [ 'class' => 'funson86\setting\Module', 'controllerNamespace' => 'funson86\setting\controllers', ], 'backup' => [ 'class' => 'yiier\backup\Module', ], 'merit' => [ 'class' => 'yiier\merit\Module', ], ], 'components' => [ 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ '/' => '/', '//' => '/', ], ], 'user' => [ 'identityClass' => 'common\models\User', 'enableAutoLogin' => true, ], 'errorHandler' => [ 'errorAction' => 'site/error', ], ], 'params' => $params, ]; ================================================ FILE: backend/config/params.php ================================================ 'admin@example.com', ]; ================================================ FILE: backend/controllers/CenterController.php ================================================ [ 'class' => AccessControl::className(), 'rules' => [ [ 'actions' => ['login', 'error'], 'allow' => true, ], [ 'actions' => ['index','test'], 'allow' => true, 'roles' => ['@'], ], ], ], 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'logout' => ['post'], ], ], ]; } /** * @inheritdoc */ public function actions() { return [ 'error' => [ 'class' => 'yii\web\ErrorAction', ], ]; } public function actionIndex() { return $this->render('index'); } public function actionTest() { return $this->render('index'); } } ================================================ FILE: backend/controllers/Controller.php ================================================ * createTime : 2016/3/10 14:39 * description: */ namespace backend\controllers; use yii\filters\AccessControl; use common\models\User; use yii\web\ForbiddenHttpException; class Controller extends \yii\web\Controller { public function behaviors() { return [ // 后台必须登录才能使用 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'allow' => true, 'roles' => ['@'], ], ], ], ]; } /** * @param \yii\base\Action $action * @return bool * @throws \yii\web\BadRequestHttpException */ public function beforeAction($action) { if (parent::beforeAction($action)) { $uniqueid = $action->controller->action->uniqueid; if (!in_array($uniqueid, ['site/login', 'site/logout', 'site/error']) && !User::currUserIsSuperAdmin()) { throw new ForbiddenHttpException; } return true; } else { return false; } } } ================================================ FILE: backend/controllers/NavController.php ================================================ Nav::find(), ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } /** * Displays a single Nav model. * @param integer $id * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new Nav model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new Nav(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing Nav model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing Nav model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the Nav model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return Nav the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = Nav::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/controllers/NavUrlController.php ================================================ NavUrl::find(), ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } /** * Displays a single NavUrl model. * @param integer $id * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new NavUrl model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new NavUrl(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing NavUrl model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing NavUrl model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the NavUrl model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return NavUrl the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = NavUrl::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/controllers/PostController.php ================================================ search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Displays a single Post model. * @param integer $id * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new Post model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new Post(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing Post model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing Post model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the Post model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return Post the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = Post::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/controllers/PostMetaController.php ================================================ search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Creates a new PostMeta model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new PostMeta(); $model->order = 999; if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect('index'); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing PostMeta model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect('index'); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing PostMeta model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the PostMeta model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return PostMeta the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = PostMeta::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/controllers/RightLinkController.php ================================================ search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Displays a single RightLink model. * @param integer $id * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new RightLink model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new RightLink(); $request = Yii::$app->request->post(); $request['RightLink']['created_user'] = Yii::$app->user->identity->username; if ($model->load($request) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing RightLink model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing RightLink model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the RightLink model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return RightLink the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = RightLink::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/controllers/SearchLogController.php ================================================ search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Deletes an existing SearchLog model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the SearchLog model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return SearchLog the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = SearchLog::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/controllers/SiteController.php ================================================ [ 'class' => AccessControl::className(), 'rules' => [ [ 'actions' => ['login', 'error'], 'allow' => true, ], [ 'actions' => ['logout', 'index'], 'allow' => true, 'roles' => ['@'], ], ], ], 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'logout' => ['post'], ], ], ]; } /** * @param \yii\base\Action $action * @return bool * @throws \yii\web\BadRequestHttpException */ public function beforeAction($action) { if (parent::beforeAction($action)) { if ($action->id == 'error' && Yii::$app->user->isGuest) { $this->layout = 'main-login'; } return true; } else { return false; } } /** * @inheritdoc */ public function actions() { return [ 'error' => [ 'class' => 'yii\web\ErrorAction', ], ]; } public function actionIndex() { return $this->render('index'); } public function actionLogin() { if (!\Yii::$app->user->isGuest) { return $this->goHome(); } $model = new LoginForm(); if ($model->load(Yii::$app->request->post()) && $model->loginAdmin()) { return $this->goBack(); } else { $data['model'] = $model; return $this->render('login', $data); } } public function actionLogout() { Yii::$app->user->logout(); return $this->goHome(); } } ================================================ FILE: backend/controllers/UserController.php ================================================ search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Displays a single User model. * @param integer $id * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new User model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new User(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing User model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing User model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the User model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return User the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = User::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: backend/models/.gitkeep ================================================ * ================================================ FILE: backend/models/PostSearch.php ================================================ joinWith(['category', 'user']); $dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => [ 'pageSize' => 20, ], 'sort' => ['defaultOrder' => [ 'order' => SORT_ASC, 'updated_at' => SORT_DESC, ]] ]); $dataProvider->sort->attributes['category_name'] = [ 'asc' => ['name' => SORT_ASC], 'desc' => ['name' => SORT_DESC], ]; $dataProvider->sort->attributes['username'] = [ 'asc' => ['username' => SORT_ASC], 'desc' => ['username' => SORT_DESC], ]; if (!($this->load($params) && $this->validate())) { return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'post_meta_id' => $this->post_meta_id, 'user_id' => $this->user_id, 'view_count' => $this->view_count, 'comment_count' => $this->comment_count, 'favorite_count' => $this->favorite_count, 'like_count' => $this->like_count, 'thanks_count' => $this->thanks_count, 'hate_count' => $this->hate_count, Post::tableName() . '.status' => $this->status, 'order' => $this->order, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', Post::tableName() . '.type', $this->type]) ->andFilterWhere(['like', 'title', $this->title]) ->andFilterWhere(['like', 'author', $this->author]) ->andFilterWhere(['like', 'excerpt', $this->excerpt]) ->andFilterWhere(['like', 'image', $this->image]) ->andFilterWhere(['like', 'content', $this->content]) ->andFilterWhere(['like', 'tags', $this->tags]) ->andFilterWhere(['like', 'name', $this->category_name]) ->andFilterWhere(['like', 'username', $this->username]); return $dataProvider; } } ================================================ FILE: backend/models/RightLinkSearch.php ================================================ $query, ]); $this->load($params); if (!$this->validate()) { return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'type' => $this->type, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'title', $this->title]) ->andFilterWhere(['like', 'url', $this->url]) ->andFilterWhere(['like', 'image', $this->image]) ->andFilterWhere(['like', 'content', $this->content]) ->andFilterWhere(['like', 'created_user', $this->created_user]); return $dataProvider; } } ================================================ FILE: backend/models/SearchLogSearch.php ================================================ joinWith('user'); $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); $dataProvider->sort->attributes['username'] = [ 'asc' => ['username' => SORT_ASC], 'desc' => ['username' => SORT_DESC], ]; $this->load($params); if (!$this->validate()) { // uncomment the following line if you do not want to return any records when validation fails // $query->where('0=1'); return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'user_id' => $this->user_id, 'created_at' => $this->created_at, ]); $query->andFilterWhere(['like', 'keyword', $this->keyword]) ->andFilterWhere(['like', 'username', $this->username]);; return $dataProvider; } } ================================================ FILE: backend/models/User.php ================================================ 255], [['auth_key'], 'string', 'max' => 32] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'username' => Yii::t('app', 'Username'), 'auth_key' => Yii::t('app', 'Auth Key'), 'password_hash' => Yii::t('app', 'Password Hash'), 'password_reset_token' => Yii::t('app', 'Password Reset Token'), 'email' => Yii::t('app', 'Email'), 'role' => Yii::t('app', 'Role'), 'status' => Yii::t('app', 'Status'), 'created_at' => Yii::t('app', 'Created At'), 'updated_at' => Yii::t('app', 'Updated At'), ]; } } ================================================ FILE: backend/runtime/.gitignore ================================================ * !.gitignore ================================================ FILE: backend/views/layouts/content.php ================================================
blocks['content-header'])) { ?>

blocks['content-header'] ?>

title !== null) { echo \yii\helpers\Html::encode($this->title); } else { echo \yii\helpers\Inflector::camel2words( \yii\helpers\Inflector::id2camel($this->context->module->id) ); echo ($this->context->module->id !== \Yii::$app->id) ? 'Module' : ''; } ?>

isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], ] ) ?>
================================================ FILE: backend/views/layouts/header.php ================================================
APP' . Yii::$app->setting->get('siteName') . '', Yii::$app->homeUrl, ['class' => 'logo']) ?>
================================================ FILE: backend/views/layouts/left.php ================================================ ================================================ FILE: backend/views/layouts/main-login.php ================================================ beginPage() ?> <?= Html::encode($this->title) ?> head() ?> beginBody() ?> endBody() ?> endPage() ?> ================================================ FILE: backend/views/layouts/main.php ================================================ controller->action->id === 'login') { /** * Do not use this code in your template. Remove it. * Instead, use the code $this->layout = '//main-login'; in your controller. */ echo $this->render( 'main-login', ['content' => $content] ); } else { if (class_exists('backend\assets\AppAsset')) { backend\assets\AppAsset::register($this); } else { app\assets\AppAsset::register($this); } dmstr\web\AdminLteAsset::register($this); $directoryAsset = Yii::$app->assetManager->getPublishedUrl('@vendor/almasaeed2010/adminlte/dist'); ?> beginPage() ?> <?= Html::encode(Yii::$app->setting->get('siteName')) ?> head() ?> beginBody() ?>
render( 'header.php', ['directoryAsset' => $directoryAsset] ) ?> render( 'left.php', ['directoryAsset' => $directoryAsset] ) ?> render( 'content.php', ['content' => $content, 'directoryAsset' => $directoryAsset] ) ?>
endBody() ?> endPage() ?> ================================================ FILE: backend/views/nav/_form.php ================================================ ================================================ FILE: backend/views/nav/create.php ================================================ title = Yii::t('app', 'Create Nav'); $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Navs'), 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/nav/index.php ================================================ title = Yii::t('app', 'Navs'); $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/nav/update.php ================================================ title = Yii::t('app', 'Update {modelClass}: ', [ 'modelClass' => 'Nav', ]) . ' ' . $model->name; $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Navs'), 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]]; $this->params['breadcrumbs'][] = Yii::t('app', 'Update'); ?> ================================================ FILE: backend/views/nav/view.php ================================================ title = $model->name; $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Navs'), 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/nav-url/_form.php ================================================ ================================================ FILE: backend/views/nav-url/create.php ================================================ title = Yii::t('app', 'Create Nav Url'); $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Nav Urls'), 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/nav-url/index.php ================================================ title = Yii::t('app', 'Nav Urls'); $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/nav-url/update.php ================================================ title = Yii::t('app', 'Update {modelClass}: ', [ 'modelClass' => 'Nav Url', ]) . ' ' . $model->title; $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Nav Urls'), 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]]; $this->params['breadcrumbs'][] = Yii::t('app', 'Update'); ?> ================================================ FILE: backend/views/nav-url/view.php ================================================ title = $model->title; $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Nav Urls'), 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/post/_form.php ================================================
field($model, 'post_meta_id')->textInput(['maxlength' => 11]) ?> field($model, 'user_id')->textInput(['maxlength' => 11]) ?> field($model, 'title')->textInput(['maxlength' => 255]) ?> field($model, 'author')->textInput(['maxlength' => 100]) ?> field($model, 'excerpt')->textInput(['maxlength' => 255]) ?> field($model, 'image')->textInput(['maxlength' => 255]) ?> field($model, 'content')->textarea(['rows' => 6]) ?> field($model, 'tags')->textInput(['maxlength' => 255]) ?> field($model, 'view_count')->textInput(['maxlength' => 11]) ?> field($model, 'comment_count')->textInput(['maxlength' => 11]) ?> field($model, 'favorite_count')->textInput(['maxlength' => 11]) ?> field($model, 'like_count')->textInput(['maxlength' => 11]) ?> field($model, 'hate_count')->textInput(['maxlength' => 11]) ?> field($model, 'status')->textInput() ?> field($model, 'order')->textInput(['maxlength' => 11]) ?> field($model, 'created_at')->textInput(['maxlength' => 11]) ?> field($model, 'updated_at')->textInput(['maxlength' => 11]) ?>
isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
================================================ FILE: backend/views/post/_search.php ================================================
['index'], 'method' => 'get', ]); ?> field($model, 'id') ?> field($model, 'post_meta_id') ?> field($model, 'user_id') ?> field($model, 'title') ?> field($model, 'author') ?> field($model, 'excerpt') ?> field($model, 'image') ?> field($model, 'content') ?> field($model, 'tags') ?> field($model, 'view_count') ?> field($model, 'comment_count') ?> field($model, 'favorite_count') ?> field($model, 'like_count') ?> field($model, 'hate_count') ?> field($model, 'status') ?> field($model, 'order') ?> field($model, 'created_at') ?> field($model, 'updated_at') ?>
'btn btn-primary']) ?> 'btn btn-default']) ?>
================================================ FILE: backend/views/post/create.php ================================================ title = 'Create Post'; $this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?>
render('_form', [ 'model' => $model, ]) ?>
================================================ FILE: backend/views/post/index.php ================================================ title = 'Posts'; $this->params['breadcrumbs'][] = $this->title; ?>
render('_search', ['model' => $searchModel]); ?>

'btn btn-success']) ?>

$dataProvider, 'filterModel' => $searchModel, 'columns' => [ [ 'attribute' => 'id', 'options' => ['width' => '10px'], ], 'title', [ 'attribute' => 'category_name', 'filter' => Html::activeTextInput($searchModel, 'category_name', ['class' => 'form-control']), 'format' => 'raw', 'value' => function ($data) { return $data->category['name']; }, ], [ 'attribute' => 'username', 'filter' => Html::activeTextInput($searchModel, 'username', ['class' => 'form-control']), 'value' => function ($data) { return $data->user['username']; }, ], // 'tags', [ 'attribute' => 'view_count', 'options' => ['width' => '10px'], ], [ 'attribute' => 'comment_count', 'options' => ['width' => '10px'], ], // [ // 'attribute' => 'favorite_count', // 'options' => ['width' => '10px'], // ], // [ // 'attribute' => 'like_count', // 'options' => ['width' => '10px'], // ], // [ // 'attribute' => 'hate_count', // 'options' => ['width' => '10px'], // ], [ 'attribute' => 'order', 'options' => ['width' => '10px'], ], [ 'class' => \common\grid\EnumColumn::className(), 'attribute' => 'status', 'filter' => Post::getStatuses(), 'enum' => Post::getStatuses(), ], 'updated_at:datetime', ['class' => 'yii\grid\ActionColumn'], ], ]); ?>
================================================ FILE: backend/views/post/update.php ================================================ title = 'Update Post: ' . ' ' . $model->title; $this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]]; $this->params['breadcrumbs'][] = 'Update'; ?>
render('_form', [ 'model' => $model, ]) ?>
================================================ FILE: backend/views/post/view.php ================================================ title = $model->title; $this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?>

$model->id], ['class' => 'btn btn-primary']) ?> $model->id], [ 'class' => 'btn btn-danger', 'data' => [ 'confirm' => 'Are you sure you want to delete this item?', 'method' => 'post', ], ]) ?>

$model, 'attributes' => [ 'id', 'post_meta_id', 'user_id', 'title', 'author', 'excerpt', 'image', 'content:ntext', 'tags', 'view_count', 'comment_count', 'favorite_count', 'like_count', 'hate_count', 'status', 'order', 'created_at', 'updated_at', ], ]) ?>
================================================ FILE: backend/views/post-meta/_form.php ================================================ ================================================ FILE: backend/views/post-meta/_search.php ================================================ ================================================ FILE: backend/views/post-meta/create.php ================================================ title = 'Create Post Meta'; $this->params['breadcrumbs'][] = ['label' => 'Post Metas', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/post-meta/index.php ================================================ title = 'Post Metas'; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/post-meta/update.php ================================================ title = 'Update Post Meta: ' . ' ' . $model->name; $this->params['breadcrumbs'][] = ['label' => 'Post Metas', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]]; $this->params['breadcrumbs'][] = 'Update'; ?> ================================================ FILE: backend/views/post-meta/view.php ================================================ title = $model->name; $this->params['breadcrumbs'][] = ['label' => 'Post Metas', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/right-link/_form.php ================================================ ================================================ FILE: backend/views/right-link/_search.php ================================================ ================================================ FILE: backend/views/right-link/create.php ================================================ title = 'Create Right Link'; $this->params['breadcrumbs'][] = ['label' => 'Right Links', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/right-link/index.php ================================================ title = '右边栏设置'; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/right-link/update.php ================================================ title = 'Update Right Link: ' . ' ' . $model->title; $this->params['breadcrumbs'][] = ['label' => 'Right Links', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]]; $this->params['breadcrumbs'][] = 'Update'; ?> ================================================ FILE: backend/views/right-link/view.php ================================================ title = $model->title; $this->params['breadcrumbs'][] = ['label' => 'Right Links', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> ================================================ FILE: backend/views/search-log/_search.php ================================================ ================================================ FILE: backend/views/search-log/index.php ================================================ title = 'Search Logs'; $this->params['breadcrumbs'][] = $this->title; ?>
render('_search', ['model' => $searchModel]); ?> $dataProvider, 'filterModel' => $searchModel, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], [ 'attribute' => 'username', 'filter' => Html::activeTextInput($searchModel, 'username', ['class' => 'form-control']), 'format' => 'raw', 'value' => function ($data) { return $data->user['username']; }, ], 'keyword', 'created_at:datetime', ['class' => 'yii\grid\ActionColumn', 'template' => '{delete}'], ], ]); ?>
================================================ FILE: backend/views/site/error.php ================================================ title = $name; ?>

The above error occurred while the Web server was processing your request. Please contact us if you think this is a server error. Thank you. Meanwhile, you may return to dashboard or try using the search form.

================================================ FILE: backend/views/site/index.php ================================================ title = 'My Yii Application'; ?>

Congratulations!

You have successfully created your Yii-powered application.

Get started with Yii

Heading

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Yii Documentation »

Heading

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Yii Forum »

Heading

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Yii Extensions »

================================================ FILE: backend/views/site/login.php ================================================ title = Yii::t('backend', 'Sign in'); $fieldOptions1 = [ 'options' => ['class' => 'form-group has-feedback'], 'inputTemplate' => "{input}" ]; $fieldOptions2 = [ 'options' => ['class' => 'form-group has-feedback'], 'inputTemplate' => "{input}" ]; ?> ================================================ FILE: backend/views/user/_form.php ================================================
field($model, 'role')->widget(Select2Widget::classname(), [ 'items' => ['' => '选择一个权限'] + User::getRoleList(), ]); ?> field($model, 'status')->widget(Select2Widget::classname(), [ 'items' => ['' => '选择一个状态'] + User::getStatusList(), ]); ?>
isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
================================================ FILE: backend/views/user/_search.php ================================================ ================================================ FILE: backend/views/user/index.php ================================================ title = 'Users'; $this->params['breadcrumbs'][] = $this->title; ?>
render('_search', ['model' => $searchModel]); ?> $dataProvider, 'filterModel' => $searchModel, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'id', 'username', 'avatar', // 'password_reset_token', // 'email:email', // 'tagline', [ 'attribute' => 'role', 'value' => function($model) { return User::getRole($model->role)['name']; }, ], // 'status', 'created_at:datetime', 'updated_at:datetime', ['class' => 'yii\grid\ActionColumn'], ], ]); ?>
================================================ FILE: backend/views/user/update.php ================================================ title = 'Update User: ' . ' ' . $model->id; $this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]]; $this->params['breadcrumbs'][] = 'Update'; ?>
render('_form', [ 'model' => $model, ]) ?>
================================================ FILE: backend/views/user/view.php ================================================ title = $model->id; $this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?>

$model->id], ['class' => 'btn btn-primary']) ?> $model->id], [ 'class' => 'btn btn-danger', 'data' => [ 'confirm' => 'Are you sure you want to delete this item?', 'method' => 'post', ], ]) ?>

$model, 'attributes' => [ 'id', 'username', 'avatar', 'auth_key', 'password_hash', 'password_reset_token', 'email:email', 'tagline', 'role', 'status', 'created_at', 'updated_at', ], ]) ?>
================================================ FILE: backend/web/.gitignore ================================================ /index.php /index-test.php ================================================ FILE: backend/web/.htaccess ================================================ # use mod_rewrite for pretty URL support RewriteEngine on # If a directory or a file exists, use the request directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # Otherwise forward the request to index.php RewriteRule . index.php # 七牛云存储的回调会传送HTTP_AUTHORIZATION认证秘钥,一定要放在最rewrite的后面.防止影响 # PHP在CGI模式下的认证信息的获取 # RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] ================================================ FILE: backend/web/assets/.gitignore ================================================ * !.gitignore ================================================ FILE: backend/web/css/sb-admin-2.css ================================================ /*! * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) * Code licensed under the Apache License v2.0. * For details, see http://www.apache.org/licenses/LICENSE-2.0. */ body { background-color: #f8f8f8; } #wrapper { width: 100%; } #page-wrapper { padding: 0 15px; min-height: 568px; background-color: #fff; } @media(min-width:768px) { #page-wrapper { position: inherit; margin: 52px 0 0 250px; padding: 0 30px; border-left: 1px solid #e7e7e7; } } .navbar-top-links li { display: inline-block; } .navbar-top-links li:last-child { margin-right: 15px; } .navbar-top-links li a { padding: 15px; min-height: 50px; } .navbar-top-links .dropdown-menu li { display: block; } .navbar-top-links .dropdown-menu li:last-child { margin-right: 0; } .navbar-top-links .dropdown-menu li a { padding: 3px 20px; min-height: 0; } .navbar-top-links .dropdown-menu li a div { white-space: normal; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { width: 310px; min-width: 0; } .navbar-top-links .dropdown-messages { margin-left: 5px; } .navbar-top-links .dropdown-tasks { margin-left: -59px; } .navbar-top-links .dropdown-alerts { margin-left: -123px; } .navbar-top-links .dropdown-user { right: 0; left: auto; } .sidebar .sidebar-nav.navbar-collapse { padding-right: 0; padding-left: 0; } .sidebar .sidebar-search { padding: 15px; } .sidebar ul li { border-bottom: 1px solid #e7e7e7; } .sidebar ul li a.active { background-color: #eee; } .sidebar .arrow { float: right; } .sidebar .fa.arrow:before { content: "\f104"; } .sidebar .active>a>.fa.arrow:before { content: "\f107"; } .sidebar .nav-second-level li, .sidebar .nav-third-level li { border-bottom: 0!important; } .sidebar .nav-second-level li a { padding-left: 37px; } .sidebar .nav-third-level li a { padding-left: 52px; } @media(min-width:768px) { .sidebar { z-index: 1; position: absolute; width: 250px; margin-top: 51px; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { margin-left: auto; } } .btn-outline { color: inherit; background-color: transparent; transition: all .5s; } .btn-primary.btn-outline { color: #428bca; } .btn-success.btn-outline { color: #5cb85c; } .btn-info.btn-outline { color: #5bc0de; } .btn-warning.btn-outline { color: #f0ad4e; } .btn-danger.btn-outline { color: #d9534f; } .btn-primary.btn-outline:hover, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover { color: #fff; } .chat { margin: 0; padding: 0; list-style: none; } .chat li { margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px dotted #999; } .chat li.left .chat-body { margin-left: 60px; } .chat li.right .chat-body { margin-right: 60px; } .chat li .chat-body p { margin: 0; } .panel .slidedown .glyphicon, .chat .glyphicon { margin-right: 5px; } .chat-panel .panel-body { height: 350px; overflow-y: scroll; } .login-panel { margin-top: 25%; } .flot-chart { display: block; height: 400px; } .flot-chart-content { width: 100%; height: 100%; } table.dataTable thead .sorting, table.dataTable thead .sorting_asc, table.dataTable thead .sorting_desc, table.dataTable thead .sorting_asc_disabled, table.dataTable thead .sorting_desc_disabled { background: 0 0; } table.dataTable thead .sorting_asc:after { content: "\f0de"; float: right; font-family: fontawesome; } table.dataTable thead .sorting_desc:after { content: "\f0dd"; float: right; font-family: fontawesome; } table.dataTable thead .sorting:after { content: "\f0dc"; float: right; font-family: fontawesome; color: rgba(50,50,50,.5); } .btn-circle { width: 30px; height: 30px; padding: 6px 0; border-radius: 15px; text-align: center; font-size: 12px; line-height: 1.428571429; } .btn-circle.btn-lg { width: 50px; height: 50px; padding: 10px 16px; border-radius: 25px; font-size: 18px; line-height: 1.33; } .btn-circle.btn-xl { width: 70px; height: 70px; padding: 10px 16px; border-radius: 35px; font-size: 24px; line-height: 1.33; } .show-grid [class^=col-] { padding-top: 10px; padding-bottom: 10px; border: 1px solid #ddd; background-color: #eee!important; } .show-grid { margin: 15px 0; } .huge { font-size: 40px; } .panel-green { border-color: #5cb85c; } .panel-green .panel-heading { border-color: #5cb85c; color: #fff; background-color: #5cb85c; } .panel-green a { color: #5cb85c; } .panel-green a:hover { color: #3d8b3d; } .panel-red { border-color: #d9534f; } .panel-red .panel-heading { border-color: #d9534f; color: #fff; background-color: #d9534f; } .panel-red a { color: #d9534f; } .panel-red a:hover { color: #b52b27; } .panel-yellow { border-color: #f0ad4e; } .panel-yellow .panel-heading { border-color: #f0ad4e; color: #fff; background-color: #f0ad4e; } .panel-yellow a { color: #f0ad4e; } .panel-yellow a:hover { color: #df8a13; } /*面包屑*/ .breadcrumb { background-color: #fff; padding: 17px 0 0; } ================================================ FILE: backend/web/css/site.css ================================================ html, body { height: 100%; } .wrap { min-height: 100%; height: auto; margin: 0 auto -60px; padding: 0 0 60px; } .wrap > .container { padding: 70px 15px 20px; } .footer { height: 60px; background-color: #f5f5f5; border-top: 1px solid #ddd; padding-top: 20px; } .jumbotron { text-align: center; background-color: transparent; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } .not-set { color: #c55; font-style: italic; } /* add sorting icons to gridview sort links */ a.asc:after, a.desc:after { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; padding-left: 5px; } a.asc:after { content: /*"\e113"*/ "\e151"; } a.desc:after { content: /*"\e114"*/ "\e152"; } .sort-numerical a.asc:after { content: "\e153"; } .sort-numerical a.desc:after { content: "\e154"; } .sort-ordinal a.asc:after { content: "\e155"; } .sort-ordinal a.desc:after { content: "\e156"; } .grid-view th { white-space: nowrap; } .hint-block { display: block; margin-top: 5px; color: #999; } .error-summary { color: #a94442; background: #fdf7f7; border-left: 3px solid #eed3d7; padding: 10px 20px; margin: 0 0 15px 0; } /*面包屑*/ #page-wrapper { margin: 52px 0 0 250px; } .breadcrumb { background-color: #fff; padding: 17px 0 0; } ================================================ FILE: backend/web/js/sb-admin-2.js ================================================ $(function() { $('#side-menu').metisMenu(); }); //Loads the correct sidebar on window load, //collapses the sidebar on window resize. // Sets the min-height of #page-wrapper to window size $(function() { $(window).bind("load resize", function() { topOffset = 50; width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; if (width < 768) { $('div.navbar-collapse').addClass('collapse') topOffset = 100; // 2-row-menu } else { $('div.navbar-collapse').removeClass('collapse') } height = (this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height; height = height - topOffset; if (height < 1) height = 1; if (height > topOffset) { $("#page-wrapper").css("min-height", (height) + "px"); } }) }) ================================================ FILE: backend/web/robots.txt ================================================ User-Agent: * Disallow: / ================================================ FILE: common/assets/AtJs.php ================================================ */ class AtJs extends AssetBundle { public $sourcePath = '@bower/At.js/dist'; public $css = [ 'css/jquery.atwho.min.css', ]; public $js = [ 'js/jquery.atwho.min.js', ]; } ================================================ FILE: common/assets/CaretJs.php ================================================ */ class CaretJs extends AssetBundle { public $sourcePath = '@bower/caret.js/dist'; public $css = []; public $js = [ 'jquery.caret.min.js', ]; } ================================================ FILE: common/assets/DropzoneJs.php ================================================ */ class DropzoneJs extends AssetBundle { public $sourcePath = '@vendor/enyo/dropzone/dist/min'; public $css = [ 'dropzone.min.css', ]; public $js = [ 'dropzone.min.js', ]; } ================================================ FILE: common/components/AssetBundle.php ================================================ YII_DEBUG // debug模式时强制拷贝 ]; } ================================================ FILE: common/components/ComposerInstaller.php ================================================ * createTime : 16/8/7 上午10:44 * description: */ namespace common\components; use yii\composer\Installer; class ComposerInstaller extends Installer { public static function initProject($event) { } } ================================================ FILE: common/components/Config.php ================================================ loadModel !== null) { $loadModel = $this->loadModel; if (!method_exists($loadModel, 'getData')) { throw new InvalidConfigException("Class {$loadModel}::getData() is undefined."); } elseif ($this->cacheTime !== false) { $cache = Yii::$app->getCache(); $cacheKey = $this->getCacheKey(); if (($data = $cache->get($cacheKey, false)) === false) { $data = $loadModel::getData(); $cache->set($cacheKey, $data, $this->cacheTime); } } else { $data = $loadModel::getData(); } $this->setData($data); // 自动保存修改后的数据 $this->autoSave && register_shutdown_function(function () { $this->saveData(); }); } } /** * 获取设置数据 * @return array */ public function getData() { return $this->_data; } /** * 设置设置数据 * @param $data */ public function setData($data) { $this->_data = $data; } /** * 设置指定键值的设置数据 * @param $name * @param $value */ public function set($name, $value) { $this->_data[$name] = $value; $this->_changedDataKeys[] = $name; } /** * 获取指定键值的设置数据 * @param $name * @param null $defaultValue * @return null */ public function get($name, $defaultValue = null) { return array_key_exists($name, $this->_data) ? $this->_data[$name] : $defaultValue; } public function setCacheKey($cacheKey) { $this->_cacheKey = $cacheKey; } public function getCacheKey() { if ($this->_cacheKey === null) { if ($this->loadModel === null) { throw new InvalidCallException('The cacheKey property of Config class must be set.'); } $this->setCacheKey('menu_' . md5($this->loadModel)); } return $this->_cacheKey; } /** * 保存数据(智慧保存修改后的数据) * @return mixed * @throws \yii\base\InvalidCallException */ public function saveData() { if ($this->loadModel === null) { throw new InvalidCallException("The data of class Config can't save. becase loadModel is not set."); } $loadModel = $this->loadModel; $data = []; foreach (array_unique($this->_changedDataKeys) as $name) { // 去重取出修改后的数据 $data[$name] = $this->get($name); } if (empty($data)) { return true; } elseif ($loadModel::saveData($data)) { $this->_changedDataKeys; //保存后可以清空了 Yii::$app->getCache()->delete($this->getCacheKey()); // 更新数据后清除旧缓存 return true; } return false; } } //class Config extends Object implements \Countable, \Iterator, \ArrayAccess //{ // /** // * model class 名,如果未指定初始数据, 会根据此值从model类中取数据 // * @var string // */ // protected $loadModel; // /** // * 如果指定了loadModel, 缓存的键值,必须注意cache键值, 缓存的键值会经过md5加密, 未指定会使用loadModel代替 // * @var string // */ // protected $cacheKey; // /** // * 缓存时间, 如果为0表示永久缓存, false 表示不缓存 // * @var int|bool // */ // protected $cacheTime = 3600; // // /** // * Config 根据数据深度来循环, 该属性会用来标记最终父级分支 // * @var array // */ // protected $topClass; // // /** // * Whether modifications to configuration data are allowed. // * // * @var bool // */ // protected $allowModifications = true; // // /** // * Number of elements in configuration data. // * // * @var int // */ // protected $count; // // /** // * Data within the configuration. // * // * @var array // */ // protected $data = array(); // // /** // * Used when unsetting values during iteration to ensure we do not skip // * the next element. // * // * @var bool // */ // protected $skipNextIteration; // // public function __construct(array $data = null, array $properties = []) // { // foreach ($properties as $name => $value) { // $this->$name = $value; // } // // if ($data !== null) { // $this->setData($data); // } else { // $this->init(); // } // } // // protected function setData(array $array) // { // foreach ($array as $key => $value) { // if (is_array($value)) { // $this->data[$key] = new static($value, [ // 'allowModifications' => $this->allowModifications, // 'topClass' => $this->topClass // ]); // } else { // $this->data[$key] = $value; // } // // $this->count++; // } // } // // public function init() // { // if ($this->loadModel !== null) { // $loadModel = $this->loadModel; // if (!method_exists($loadModel, 'getData')) { // throw new InvalidConfigException("Class {$loadModel}::getData() is undefined."); // } elseif ($this->cacheTime === false) { // $data = $loadModel::getData(); // } else { // $cache = Yii::$app->get('cache'); // $cacheKey = md5($this->cacheKey ? $this->cacheKey : $this->loadModel); // if (($data = $cache->get($cacheKey, false)) === false) { // $data = $loadModel::getData(); // $cache->set($cacheKey, $data, $this->cacheTime); // } // } // $this->setData($data); // } // } // // /** // * Retrieve a value and return $default if there is no element set. // * // * @param string $name // * @param mixed $default // * @return mixed // */ // public function get($name, $default = null) // { // if (array_key_exists($name, $this->data)) { // return $this->data[$name]; // } // // return $default; // } // // /** // * Magic function so that $obj->value will work. // * // * @param string $name // * @return mixed // */ // public function __get($name) // { // return $this->get($name); // } // // /** // * Set a value in the config. // * // * Only allow setting of a property if $allowModifications was set to true // * on construction. Otherwise, throw an exception. // * // * @param string $name // * @param mixed $value // * @return void // * @throws Exception\RuntimeException // */ // public function __set($name, $value) // { // if ($this->allowModifications) { // // if (is_array($value)) { // $value = new static($value, true); // } // // if (null === $name) { // $this->data[] = $value; // } else { // $this->data[$name] = $value; // } // // $this->count++; // } else { // throw new Exception\RuntimeException('Config is read only'); // } // } // // /** // * Deep clone of this instance to ensure that nested Zend\Configs are also // * cloned. // * // * @return void // */ // public function __clone() // { // $array = array(); // // foreach ($this->data as $key => $value) { // if ($value instanceof self) { // $array[$key] = clone $value; // } else { // $array[$key] = $value; // } // } // // $this->data = $array; // } // // /** // * Return an associative array of the stored data. // * // * @return array // */ // public function toArray() // { // $array = array(); // $data = $this->data; // // /** @var self $value */ // foreach ($data as $key => $value) { // if ($value instanceof self) { // $array[$key] = $value->toArray(); // } else { // $array[$key] = $value; // } // } // // return $array; // } // // /** // * isset() overloading // * // * @param string $name // * @return bool // */ // public function __isset($name) // { // return isset($this->data[$name]); // } // // /** // * unset() overloading // * // * @param string $name // * @return void // * @throws Exception\InvalidArgumentException // */ // public function __unset($name) // { // if (!$this->allowModifications) { // throw new Exception\InvalidArgumentException('Config is read only'); // } elseif (isset($this->data[$name])) { // unset($this->data[$name]); // $this->count--; // $this->skipNextIteration = true; // } // } // // /** // * count(): defined by Countable interface. // * // * @see Countable::count() // * @return int // */ // public function count() // { // return $this->count; // } // // /** // * current(): defined by Iterator interface. // * // * @see Iterator::current() // * @return mixed // */ // public function current() // { // $this->skipNextIteration = false; // return current($this->data); // } // // /** // * key(): defined by Iterator interface. // * // * @see Iterator::key() // * @return mixed // */ // public function key() // { // return key($this->data); // } // // /** // * next(): defined by Iterator interface. // * // * @see Iterator::next() // * @return void // */ // public function next() // { // if ($this->skipNextIteration) { // $this->skipNextIteration = false; // return; // } // // next($this->data); // } // // /** // * rewind(): defined by Iterator interface. // * // * @see Iterator::rewind() // * @return void // */ // public function rewind() // { // $this->skipNextIteration = false; // reset($this->data); // } // // /** // * valid(): defined by Iterator interface. // * // * @see Iterator::valid() // * @return bool // */ // public function valid() // { // return ($this->key() !== null); // } // // /** // * offsetExists(): defined by ArrayAccess interface. // * // * @see ArrayAccess::offsetExists() // * @param mixed $offset // * @return bool // */ // public function offsetExists($offset) // { // return $this->__isset($offset); // } // // /** // * offsetGet(): defined by ArrayAccess interface. // * // * @see ArrayAccess::offsetGet() // * @param mixed $offset // * @return mixed // */ // public function offsetGet($offset) // { // return $this->__get($offset); // } // // /** // * offsetSet(): defined by ArrayAccess interface. // * // * @see ArrayAccess::offsetSet() // * @param mixed $offset // * @param mixed $value // * @return void // */ // public function offsetSet($offset, $value) // { // $this->__set($offset, $value); // } // // /** // * offsetUnset(): defined by ArrayAccess interface. // * // * @see ArrayAccess::offsetUnset() // * @param mixed $offset // * @return void // */ // public function offsetUnset($offset) // { // $this->__unset($offset); // } // // /** // * Merge another Config with this one. // * // * For duplicate keys, the following will be performed: // * - Nested Configs will be recursively merged. // * - Items in $merge with INTEGER keys will be appended. // * - Items in $merge with STRING keys will overwrite current values. // * // * @param Config $merge // * @return Config // */ // public function merge(Config $merge) // { // /** @var Config $value */ // foreach ($merge as $key => $value) { // if (array_key_exists($key, $this->data)) { // if (is_int($key)) { // $this->data[] = $value; // } elseif ($value instanceof self && $this->data[$key] instanceof self) { // $this->data[$key]->merge($value); // } else { // if ($value instanceof self) { // $this->data[$key] = new static($value->toArray(), $this->allowModifications); // } else { // $this->data[$key] = $value; // } // } // } else { // if ($value instanceof self) { // $this->data[$key] = new static($value->toArray(), $this->allowModifications); // } else { // $this->data[$key] = $value; // } // // $this->count++; // } // } // // return $this; // } // // /** // * Prevent any more modifications being made to this instance. // * // * Useful after merge() has been used to merge multiple Config objects // * into one object which should then not be modified again. // * // * @return void // */ // public function setReadOnly() // { // $this->allowModifications = false; // // /** @var Config $value */ // foreach ($this->data as $value) { // if ($value instanceof self) { // $value->setReadOnly(); // } // } // } // // /** // * Returns whether this Config object is read only or not. // * // * @return bool // */ // public function isReadOnly() // { // return !$this->allowModifications; // } //} ================================================ FILE: common/components/Controller.php ================================================ [ 'class' => 'yiier\returnUrl\ReturnUrl', 'uniqueIds' => ['site/qr', 'site/login', 'user/security/auth', 'site/reset-password'] ], MeritBehavior::className(), ]); } public function beforeAction($action) { if (parent::beforeAction($action)) { return true; } else { return false; } } public function afterAction($action, $result) { if (!Yii::$app->user->isGuest) { $actionName = "frontend@{$this->module->id}_{$this->id}_{$action->id}"; } return parent::afterAction($action, $result); } /** * 显示flash信息 * @param $message string 信息显示内容 * @param string $type 信息显示类型, ['info', 'success', 'error', 'warning'] * @param null $url 跳转地址 * @throws \yii\base\ExitException */ public function flash($message, $type = 'info', $url = null) { Yii::$app->getSession()->setFlash($type, $message); if ($url !== null) { Yii::$app->end(0, $this->redirect($url)); } } /** * @param $message string 信息显示内容 * @param string $type 信息显示类型, ['info', 'success', 'error', 'warning'] * @param null $redirect 跳转地址 * @param null $resultType 信息显示格式 * @return array|string */ public function message($message, $type = 'info', $redirect = null, $resultType = null) { $resultType === null && $resultType = Yii::$app->getRequest()->getIsAjax() ? 'json' : 'html'; is_array($redirect) && $redirect = Url::to($redirect); $data = [ 'type' => $type, 'message' => $message, 'redirect' => $redirect ]; if ($resultType === 'json') { Yii::$app->getResponse()->format = Response::FORMAT_JSON; return $data; } elseif ($resultType === 'html') { return $this->render('/common/message', $data); } } // public $ajaxLayout = '/ajaxMain'; // public function findLayoutFile($view) // { // if (($this->layout === null) && ($this->ajaxLayout !== false) && Yii::$app->getRequest()->getIsAjax()) { // $this->layout = $this->ajaxLayout; // } // return parent::findLayoutFile($view); // } } ================================================ FILE: common/components/DbAuthManager.php ================================================ from($this->itemChildTable) ->join('INNER JOIN', $this->itemTable, implode(' AND ', [ $this->itemTable . '.name=' . $this->itemChildTable . '.child', $this->itemTable . '.type = ' . $type ])); $parents = []; foreach ($query->all($this->db) as $row) { $parents[$row['parent']][] = $row['child']; } return $parents; } /** * @params bool $recursive 是否递归显示子角色权限 * @inheritdoc */ public function getPermissionsByRole($roleName, $recursive = true) { $childrenList = $recursive ? $this->getChildrenList() : $this->getChildrenListOfType(Item::TYPE_PERMISSION); $result = []; $this->getChildrenRecursive($roleName, $childrenList, $result); if (empty($result)) { return []; } $query = (new Query)->from($this->itemTable)->where([ 'type' => Item::TYPE_PERMISSION, 'name' => array_keys($result), ]); $permissions = []; foreach ($query->all($this->db) as $row) { $permissions[$row['name']] = $this->populateItem($row); } return $permissions; } } ================================================ FILE: common/components/FileTarget.php ================================================ * createTime : 2015/12/22 18:13 * description: */ namespace common\components; use Yii; use yii\helpers\FileHelper; class FileTarget extends \yii\log\FileTarget { /** * @var bool 是否启用日志前缀 (@app/runtime/logs/error/20151223_app.log) */ public $enableDatePrefix = false; /** * @var bool 启用日志等级目录 */ public $enableCategoryDir = false; private $_logFilePath = ''; public function init() { if ($this->logFile === null) { $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log'; } else { $this->logFile = Yii::getAlias($this->logFile); } $this->_logFilePath = dirname($this->logFile); // 启用日志前缀 if ($this->enableDatePrefix) { $filename = basename($this->logFile); $this->logFile = $this->_logFilePath . '/' . date('Ymd') . '_' . $filename; } if (!is_dir($this->_logFilePath)) { FileHelper::createDirectory($this->_logFilePath, $this->dirMode, true); } if ($this->maxLogFiles < 1) { $this->maxLogFiles = 1; } if ($this->maxFileSize < 1) { $this->maxFileSize = 1; } } } ================================================ FILE: common/components/GlobalFunctions.php ================================================ get($component, $throwException); } } if (!function_exists('t')) { /** * i18n 国际化 * @param $category * @param $message * @param array $params * @param null $language * @return string */ function t($category, $message, $params = [], $language = null) { return Yii::t($category, $message, $params, $language); } } if (!function_exists('user')) { /** * User组件或者(设置|返回)identity属性 * * @param null|string|array $attribute idenity属性 * @return \yii\web\User|string|array */ function user($attribute = null) { if ($attribute === null) { return Yii::$app->getUser(); } if (is_array($attribute)) { return Yii::$app->getUser()->getIdentity()->setAttributes($attribute); } return Yii::$app->getUser()->getIdentity()->{$attribute}; } } if (!function_exists('request')) { /** * Request组件或者通过Request组件获取GET值 * * @param string $key * @param mixed $default * @return \yii\web\Request|string|array */ function request($key = null, $default = null) { if ($key === null) { return Yii::$app->getRequest(); } return Yii::$app->getRequest()->getQueryParam($key, $default); } } if (!function_exists('response')) { /** * Response组件或者通过Response组织内容 * * @param string $content 响应内容 * @param string $format 响应格式 * @return \yii\web\Response */ function response($content = '', $format = Response::FORMAT_HTML, $status = null) { $response = Yii::$app->getResponse(); if (func_num_args() !== 0) { $response->format = $format; if ($status !== null) { $response->setStatusCode($status); } if ($format === Response::FORMAT_HTML) { $response->content = $content; } else { $response->data = $content; } } return $response; } } if (!function_exists('params')) { /** * params 组件或者通过 params 组件获取GET值 * @param $key * @return mixed|\yii\web\Session */ function params($key) { return Yii::$app->params[$key]; } } /** * @param $message * @param bool|true $debug */ function pr($message, $debug = true) { echo '
';
    print_r($message);
    echo '
'; if ($debug) { die; } } ================================================ FILE: common/components/Mailer.php ================================================ set('mailer', [ 'class' => 'yii\swiftmailer\Mailer', 'viewPath' => '@common/mail', 'transport' => [ 'class' => 'Swift_SmtpTransport', 'host' => \Yii::$app->setting->get('smtpHost'), 'username' => \Yii::$app->setting->get('smtpUser'), 'password' => \Yii::$app->setting->get('smtpPassword'), 'port' => \Yii::$app->setting->get('smtpPort'), // 'mail' => \Yii::$app->setting->get('smtpMail'), // 显示地址 'encryption' => 'tls', ], ]); } /** * Sends an email to a user with credentials and confirmation link. * * @param User $user * @param Token $token * @return bool */ public function sendWelcomeMessage(User $user, Token $token = null) { return $this->sendMessage($user->email, $this->welcomeSubject, 'welcome', ['user' => $user, 'token' => $token] ); } /** * @param string $to * @param string $subject * @param string $view * @param array $params * @return bool */ protected function sendMessage($to, $subject, $view, $params = []) { $mailer = \Yii::$app->mailer; $mailer->viewPath = $this->viewPath; $mailer->getView()->theme = \Yii::$app->view->theme; return $mailer->compose(['html' => $view, 'text' => 'text/' . $view], $params) ->setTo($to) ->setFrom($this->sender) ->setSubject($subject) ->send(); } /** * Sends an email to a user with confirmation link. * * @param User $user * @param Token $token * @return bool */ public function sendConfirmationMessage(User $user, Token $token) { return $this->sendMessage($user->email, $this->confirmationSubject, 'confirmation', ['user' => $user, 'token' => $token] ); } /** * Sends an email to a user with reconfirmation link. * * @param User $user * @param Token $token * @return bool */ public function sendReconfirmationMessage(User $user, Token $token) { if ($token->type == Token::TYPE_CONFIRM_NEW_EMAIL) { $email = $user->unconfirmed_email; } else { $email = $user->email; } return $this->sendMessage($email, $this->reconfirmationSubject, 'reconfirmation', ['user' => $user, 'token' => $token] ); } /** * Sends an email to a user with recovery link. * * @param User $user * @param Token $token * @return bool */ public function sendRecoveryMessage(User $user, Token $token) { return $this->sendMessage($user->email, $this->recoverySubject, 'recovery', ['user' => $user, 'token' => $token] ); } } ================================================ FILE: common/components/db/ActiveRecord.php ================================================ [ 'class' => 'yii\behaviors\TimestampBehavior', ], ]; } } ================================================ FILE: common/components/db/Command.php ================================================ db->getQueryBuilder()->batchInsert($table, $columns, $rows); return $this->setSql('REPLACE' . substr($sql, strpos($sql, ' '))); } } ?> ================================================ FILE: common/components/db/Connection.php ================================================ open(); $command = new Command([ // 使用了继承了之后的Command类.. 'db' => $this, 'sql' => $sql, ]); return $command->bindValues($params); } } ================================================ FILE: common/components/db/Migration.php ================================================ db->driverName === 'mysql') { //Mysql 表选项 $engine = $this->useTransaction ? 'InnoDB' : 'MyISAM'; $this->tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=' . $engine; } } } ================================================ FILE: common/config/.gitignore ================================================ main-local.php params-local.php db.php ================================================ FILE: common/config/bootstrap.php ================================================ [ // '@bower' => '@vendor/bower-asset', // '@npm' => '@vendor/npm-asset', // ], 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'timeZone' => 'Asia/Shanghai', //time zone affect the formatter datetime format 'language' => 'zh-CN', 'modules' => [ ], 'components' => [ 'formatter' => [ //for the showing of date datetime 'dateFormat' => 'yyyy-MM-dd', 'locale' => 'zh-CN', 'datetimeFormat' => 'yyyy-MM-dd HH:mm:ss', 'decimalSeparator' => ',', 'thousandSeparator' => ' ', 'currencyCode' => 'CNY', ], 'setting' => [ 'class' => 'funson86\setting\Setting', ], 'qr' => [ 'class' => '\Da\QrCode\Component\QrCodeComponent', // 'label' => '2amigos consulting group llc', 'size' => 500 // big and nice :D // ... you can configure more properties of the component here ], 'mailer' => [ 'class' => 'yii\swiftmailer\Mailer', 'viewPath' => '@common/mail', ], 'cache' => [ 'class' => 'yii\caching\FileCache', //'class' => 'yii\caching\ApcCache', 'cachePath' => '@backend/runtime/cache', ], 'slack' => [ 'httpclient' => ['class' => 'yii\httpclient\Client'], 'class' => 'understeam\slack\Client', 'url' => '', ], // 'assetManager' => [ // 'linkAssets' => true, // ], 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ [ 'class' => 'understeam\slack\LogTarget', 'enabled' => false, // 是否开启错误信息发送 Slack 服务,默认否 'levels' => ['error'], 'categories' => [ 'yii\db\*', 'yii\web\HttpException:*', ], 'except' => [ 'yii\web\HttpException:404', // 除了404错误 ], 'exportInterval' => 1, // Send logs on every message 'logVars' => [], ], [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning', 'info', 'trace'], ], /** * 错误级别日志:当某些需要立马解决的致命问题发生的时候,调用此方法记录相关信息。 * 使用方法:Yii::error() */ [ 'class' => 'common\components\FileTarget', // 日志等级 'levels' => ['error'], // 被收集记录的额外数据 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/error/app.log', // 是否开启日志 (@app/runtime/logs/error/20151223_app.log) 'enableDatePrefix' => true, ], /** * 警告级别日志:当某些期望之外的事情发生的时候,使用该方法。 * 使用方法:Yii::warning() */ [ 'class' => 'common\components\FileTarget', // 日志等级 'levels' => ['warning'], // 被收集记录的额外数据 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/warning/app.log', // 是否开启日志 (@app/runtime/logs/warning/20151223_app.log) 'enableDatePrefix' => true, ], /** * info 级别日志:在某些位置记录一些比较有用的信息的时候使用。 * 使用方法:Yii::info() */ [ 'class' => 'common\components\FileTarget', 'enabled' => YII_DEBUG, // debug 模式才开启 // 日志等级 'levels' => ['info'], // 被收集记录的额外数据 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/info/app.log', // 是否开启日志 (@app/runtime/logs/info/20151223_app.log) 'enableDatePrefix' => true, ], /** * trace 级别日志:记录关于某段代码运行的相关消息。主要是用于开发环境。 * 使用方法:Yii::trace() */ [ 'class' => 'common\components\FileTarget', 'enabled' => YII_DEBUG, // debug 模式才开启 // 日志等级 'levels' => ['trace'], // 被收集记录的额外数据 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/trace/app.log', // 是否开启日志 (@app/runtime/logs/trace/20151223_app.log) 'enableDatePrefix' => true, ], ], ], 'session' => [ 'class' => 'yii\web\DbSession', ], 'db' => require(__DIR__ . '/db.php'), 'i18n' => [ 'translations' => [ 'frontend*' => [ 'class' => 'yii\i18n\PhpMessageSource', 'basePath' => '@common/messages', ], 'backend*' => [ 'class' => 'yii\i18n\PhpMessageSource', 'basePath' => '@common/messages', ], 'common*' => [ 'class' => 'yii\i18n\PhpMessageSource', 'basePath' => '@common/messages', ], ], ], ], ]; ================================================ FILE: common/config/params.php ================================================ 'caizhenghai@gmail.com', 'backupEmail' => 'caizhenghai@qq.com', 'supportEmail' => 'forecho@foxmail.com', 'user.passwordResetTokenExpire' => 3600, // 'avatarPath' => Yii::$app->basePath . '/uploads/avatars/', // 'avatarUrl' => Yii::$app->urlManager->baseUrl . '/uploads/avatars/', 'avatarPath' => '/web/uploads/avatars/', 'avatarUrl' => '/uploads/avatars/', 'avatarCachePath' => '/web/uploads/avatars/cache/', 'avatarCacheUrl' => '/uploads/avatars/cache/', 'icon-framework' => 'fa', // Font Awesome Icon framework 'qrCodePath' => '/web/uploads/qr-code/', 'qrCodeUrl' => '/uploads/qr-code/', 'newUserPostLimit' => 0, // 防止 spam,可限制新注册用户多少秒之后才能发帖,默认 0 代表不限制,单位是秒 'smToken' => '', // https://sm.ms/home/apitoken 'createPostNeedVerify' => false, // 发帖是否需要审核 ]; ================================================ FILE: common/grid/EnumColumn.php ================================================ 'common\grid\EnumColumn', * 'attribute' => 'role', * 'enum' => User::getRoles() * ] * @package common\components\grid */ class EnumColumn extends DataColumn { /** * @var array List of value => name pairs */ public $enum = []; /** * @var bool */ public $loadFilterDefaultValues = true; /** * @inheritdoc */ public function init() { if ($this->loadFilterDefaultValues && $this->filter === null) { $this->filter = $this->enum; } } /** * @param mixed $model * @param mixed $key * @param int $index * @return mixed */ public function getDataCellValue($model, $key, $index) { $value = parent::getDataCellValue($model, $key, $index); return ArrayHelper::getValue($this->enum, $value, $value); } } ================================================ FILE: common/helpers/Arr.php ================================================ * createTime : 16/3/19 上午9:57 * description: */ namespace common\helpers; use yii\helpers\ArrayHelper; class Arr extends ArrayHelper { /** * 随机筛选$num个数组 * @param array $arr * @param int $num * @return array|false */ public static function arrayRandomAssoc(Array $arr, $num = 1) { if (!$arr) { return false; } $keys = array_keys($arr); shuffle($keys); $r = []; for ($i = 0; $i < $num; $i++) { $r[$keys[$i]] = $arr[$keys[$i]]; } return $r; } } ================================================ FILE: common/helpers/Avatar.php ================================================ * createTime : 15/5/17 下午4:14 * description: */ namespace common\helpers; class Avatar { public $email; public $size; public function __construct($email, $size = 50) { $this->email = $email; $this->size = $size; } public function getAvater() { // 说明: Gravatar 随机头像太丑了 所以使用 Identicon 随机头像 // TODO 保存头像图片 加缓存 // return $this->getGravatar(); $identicon = new \Identicon\Identicon(); return $identicon->getImageDataUri($this->email, $this->size); } /** * 根据 email 获取 gravatar 头像的地址 * @return string */ private function getGravatar() { $hash = md5(strtolower(trim($this->email))); return sprintf('http://gravatar.com/avatar/%s?s=%d&d=%s', $hash, $this->size, 'identicon'); } /** * 验证email是否有对应的 Gravatar 头像(效率太低) * @return bool */ private function validateGravatar() { $hash = md5(strtolower(trim($this->email))); $uri = 'http://gravatar.com/avatar/' . $hash . '?d=404'; $headers = @get_headers($uri); if (!preg_match("|200|", $headers[0])) { return false; } else { return true; } } } ================================================ FILE: common/helpers/Formatter.php ================================================ * createTime : 2015/8/4 14:19 * description: */ namespace common\helpers; class Formatter { const DATE_FORMAT = 'php:Y-m-d'; const DATETIME_FORMAT = 'php:Y-m-d H:i:s'; const TIME_FORMAT = 'php:H:i:s'; public static function convert($dateStr, $type = 'date', $format = null) { if ($type === 'datetime') { $fmt = ($format == null) ? self::DATETIME_FORMAT : $format; } elseif ($type === 'time') { $fmt = ($format == null) ? self::TIME_FORMAT : $format; } else { $fmt = ($format == null) ? self::DATE_FORMAT : $format; } return \Yii::$app->formatter->asDate($dateStr, $fmt); } /** * 相对时间 * @param $dateStr * @return string */ public static function relative($dateStr) { return \Yii::$app->formatter->asRelativeTime($dateStr); } } ================================================ FILE: common/helpers/UploadHelper.php ================================================ beginPage() ?> <?= Html::encode($this->title) ?> head() ?> beginBody() ?> endBody() ?> endPage() ?> ================================================ FILE: common/mail/passwordResetToken.php ================================================ urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); ?> Hello username) ?>, Follow the link below to reset your password: ================================================ FILE: common/messages/pt-BR/backend.php ================================================ 'EXCLUÍDO', 'ACTIVE' => 'ATIVO', 'EXCELLENT' => 'EXCELENTE', 'TOP' => 'TOP', 'Username' => 'Usuário', 'Password' => 'Senha', 'Remember Me' => 'Lembrar-me', 'Sign in' => 'Acessar', 'Article View' => 'Ver artigo', 'Page View' => 'Ver Página', '"{attribute}" must be a valid JSON' => '"{attribute}" precisa ser JSON válido', 'Active' => 'Ativo', 'Article ID' => 'ID artigo', 'Author' => 'Autor', 'Base URL' => 'URL Base', 'Base Url' => 'Url Base', 'Body' => 'Corpo', 'Caption' => 'Legenda', 'Carousel ID' => 'Carrossel ID', 'Category' => 'Categoria', 'Comment' => 'Comentario', 'Component' => 'Componente', 'Config' => 'Configuracão', 'Created At' => 'Criado Em', 'Created at' => 'Criado em', 'Deleted' => 'Excluído', 'Down to maintenance.' => 'Desativado para manutenção', 'E-mail' => 'E-mail', 'File Type' => 'Tipo de arquivo', 'Firstname' => 'Nome', 'Gender' => 'Genero', 'ID' => 'ID', 'Image' => 'Imagen', 'Key' => 'Chave', 'Last login' => 'Ultimo Login', 'Lastname' => 'Ultimo Nome', 'Locale' => 'Localidade', 'Middlename' => 'Nome do meio', 'Name' => 'Nome', 'Order' => 'Ordem', 'Parent Category' => 'Categoria Pai', 'Path' => 'Caminho', 'Picture' => 'Imagen', 'Published' => 'Publicado', 'Published At' => 'Publicado em', 'Size' => 'Tamanho', 'Slug' => 'Slug', 'Status' => 'Status', 'Thumbnail' => 'Miniatura', 'Title' => 'Título', 'Type' => 'Tipo', 'Updated At' => 'Atualizado Em', 'Updated at' => 'Atualizado em', 'Updater' => 'Atualizado por', 'Upload Ip' => 'IP', 'Url' => 'Url', 'User ID' => 'ID do Usuario', 'Value' => 'Valor', ]; ================================================ FILE: common/messages/pt-BR/common.php ================================================ 'EXCLUIDO', 'ACTIVE' => 'ATIVO', 'EXCELLENT' => 'EXCELENTE', 'TOP' => 'TOP', 'Username' => 'Usuário', 'Password' => 'Senha', 'Remember Me' => 'Lembrar-me', 'Incorrect username or password.' => 'Usuário ou senha inválido', 'You don\'t have permission to login.' => 'Você não tem permissão pra autenticar', 'User ID' => 'ID do usuário', 'Type' => 'Tipo', 'Merit' => 'Mérito', 'Created At' => 'Criado em', 'Updated At' => 'Alterado em', 'Merit Template ID' => 'Cod Template Merit', 'Description' => 'Descrição', 'Action Type' => 'Tipo de Ação', 'Increment' => 'Incremento', ]; ================================================ FILE: common/messages/pt-BR/frontend.php ================================================ 'criado em {datetime}', 'reply_at {datetime}' => 'respondido em {datetime}', 'last_by' => 'Por', ]; ================================================ FILE: common/messages/zh-CN/backend.php ================================================ '已删除', 'ACTIVE' => '正常', 'EXCELLENT' => '推荐', 'TOP' => '置顶', 'Username' => '用户名', 'Password' => '密码', 'Remember Me' => '记住我', 'Sign in' => '登录', 'Article View' => 'Vista de Artículo', 'Page View' => 'Vista de Página', '"{attribute}" must be a valid JSON' => '"{attribute}" debe de ser JSON válido', 'Active' => 'Activo', 'Article ID' => 'ID Artículo', 'Author' => 'Autor', 'Base URL' => 'URL Base', 'Base Url' => 'Url Base', 'Body' => 'Cuerpo', 'Caption' => 'Leyenda', 'Carousel ID' => 'Carrusel ID', 'Category' => 'Categoría', 'Comment' => 'Comentario', 'Component' => 'Componente', 'Config' => 'Configuración', 'Created At' => 'Creado el', 'Created at' => 'Creado el', 'Deleted' => 'Borrado', 'Down to maintenance.' => 'Apagado por mantenimiento.', 'E-mail' => 'E-mail', 'File Type' => 'Tipo fichero', 'Firstname' => 'Nombre', 'Gender' => 'Género', 'ID' => 'ID', 'Image' => 'Imagen', 'Key' => 'Clave', 'Last login' => 'ültimo Login', 'Lastname' => 'Apellido', 'Locale' => 'Pais', 'Middlename' => 'Segundo Nombre', 'Name' => 'Nombre', 'Order' => 'Orden', 'Parent Category' => 'Categoría Padre', 'Path' => 'Ruta', 'Picture' => 'Imagen', 'Published' => 'Publicado', 'Published At' => 'Publicado el', 'Size' => 'Tamaño', 'Slug' => 'Slug', 'Status' => 'Estado', 'Thumbnail' => 'Miniatura', 'Title' => 'Título', 'Type' => 'Tipo', 'Updated At' => 'Actualizado el', 'Updated at' => 'Actualizado el', 'Updater' => 'Actualizado por', 'Upload Ip' => 'IP', 'Url' => 'Url', 'User ID' => 'Usuario ID', 'Value' => 'Valor', ]; ================================================ FILE: common/messages/zh-CN/common.php ================================================ '已删除', 'ACTIVE' => '正常', 'EXCELLENT' => '推荐', 'TOP' => '置顶', 'Username' => '用户名/邮箱', 'Password' => '密码', 'Remember Me' => '记住我', 'Incorrect username or password.' => '用户名密码验证失败。', 'You don\'t have permission to login.' => '你没有登录权限。', 'User ID' => '用户 ID', 'Type' => '分类', 'Merit' => '总值', 'Created At' => '创建时间', 'Updated At' => '更新时间', 'Merit Template ID' => '模板ID', 'Description' => '描述', 'Action Type' => '操作类型 0减去 1新增', 'Increment' => '变化值', ]; ================================================ FILE: common/messages/zh-CN/frontend.php ================================================ '于 {datetime} 发布', 'reply_at {datetime}' => '于 {datetime} 回复', 'last_by' => '最后由', ]; ================================================ FILE: common/models/LoginForm.php ================================================ hasErrors()) { $user = $this->getUser(); if (!$user || !$user->validatePassword($this->password)) { $this->addError($attribute, Yii::t('common', 'Incorrect username or password.')); } } } /** * Logs in a user using the provided username and password. * * @return boolean whether the user is logged in successfully */ public function login() { if ($this->validate()) { return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); } else { return false; } } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'username' => Yii::t('common', 'Username'), 'password' => Yii::t('common', 'Password'), 'rememberMe' => Yii::t('common', 'Remember Me'), ]; } /** * email 邮箱登录 * @user onyony * @return bool|null|static */ public function getUser() { if ($this->_user === false) { if (strpos($this->username, "@")) $this->_user = User::findByEmail($this->username); //email 登录 else $this->_user = User::findByUsername($this->username); } return $this->_user; } /** * 登陆之后更新用户资料 * @return bool */ public function updateUserInfo() { /** @var UserInfo $model */ $model = UserInfo::findOne(['user_id' => Yii::$app->user->getId()]); $model->login_count += 1; $model->prev_login_time = $model->last_login_time; $model->prev_login_ip = $model->last_login_ip; $model->last_login_time = time(); $model->last_login_ip = Yii::$app->getRequest()->getUserIP(); if (!Yii::$app->session->isActive) { Yii::$app->session->open(); } $model->session_id = Yii::$app->session->id; Yii::$app->session->close(); if ($model->save()) { return true; } else { return false; } } public function loginAdmin() { if ($this->validate()) { if (User::isSuperAdmin($this->username)) { return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); } $this->addError('username', 'You don\'t have permission to login.'); } else { $this->addError('password', Yii::t('common', 'Incorrect username or password.')); } return false; } } ================================================ FILE: common/models/Nav.php ================================================ 50] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'name' => Yii::t('app', 'Name'), 'alias' => Yii::t('app', 'Alias'), 'order' => Yii::t('app', 'Order'), 'created_at' => Yii::t('app', 'Created At'), 'updated_at' => Yii::t('app', 'Updated At'), ]; } public static function getNavList() { $data_array = ArrayHelper::map(static::find()->orderBy(['order' => SORT_ASC])->all(), 'id', 'name'); return $data_array; } } ================================================ FILE: common/models/NavUrl.php ================================================ 255], [['url'], 'string', 'max' => 225] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'nav_id' => Yii::t('app', 'Nav ID'), 'title' => Yii::t('app', 'Title'), 'url' => Yii::t('app', 'Url'), 'description' => Yii::t('app', 'Description'), 'order' => Yii::t('app', 'Order'), 'user_id' => Yii::t('app', 'User ID'), 'created_at' => Yii::t('app', 'Created At'), 'updated_at' => Yii::t('app', 'Updated At'), ]; } } ================================================ FILE: common/models/Post.php ================================================ 2], ['content', 'validateLimitPostTime'], [['type'], 'string', 'max' => 32], [['last_comment_username'], 'string', 'max' => 20], [['title'], 'string', 'max' => 50, 'min' => 2], [['excerpt', 'image'], 'string', 'max' => 255], [['author'], 'string', 'max' => 100], [['cc', 'tags'], 'safe'], ['content', SpamValidator::className(), 'message' => '请勿发表垃圾内容'], ['title', SpamValidator::className(), 'message' => '请勿发表垃圾内容'], ]; } /** * 验证新用户是否能发帖 * @param $attribute */ public function validateLimitPostTime($attribute) { if ($time = $this->limitPostTime()) { $this->addError($attribute, "新注册用户只能回帖,{$time}秒之后才能发帖。"); } } /** * 新用户N秒之后才能发帖 * @return bool|int */ public function limitPostTime() { $userCreatedAt = Yii::$app->user->identity['created_at']; $newUserPostLimit = params('newUserPostLimit'); if ($newUserPostLimit && time() - $userCreatedAt < $newUserPostLimit) { return $newUserPostLimit - (time() - $userCreatedAt); } return false; } /** * 限制发帖间隔 * @return bool|int */ public function limitPostingIntervalTime() { $lastPostCreatedAt = Post::find()->select('created_at')->where(['type' => self::TYPE_TOPIC])->orderBy(['created_at' => SORT_DESC])->scalar(); $postingIntervalLimit = params('postingIntervalLimit'); if ($postingIntervalLimit && time() - $lastPostCreatedAt < $postingIntervalLimit) { return $postingIntervalLimit - (time() - $lastPostCreatedAt); } return false; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'type' => '内容类型', 'post_meta_id' => '分类', 'category_name' => '分类', 'user_id' => '用户ID', 'username' => '用户', 'title' => '标题', 'author' => '作者', 'excerpt' => '摘要', 'image' => '封面图片', 'content' => '内容', 'tags' => '标签', 'last_comment_username' => '最后回复用户', 'last_comment_time' => '最后评论时间', 'view_count' => '查看数', 'comment_count' => '评论数', 'favorite_count' => '收藏数', 'like_count' => '喜欢数', 'thanks_count' => '感谢数', 'hate_count' => '讨厌数', 'status' => '状态', 'order' => '排序', 'created_at' => '创建时间', 'updated_at' => '修改时间', 'cc' => '注明版权信息(原创文章欢迎使用)', ]; } public function getUser() { return $this->hasOne(User::className(), ['id' => 'user_id']); } public function getUserInfo() { return $this->hasOne(UserInfo::className(), ['user_id' => 'user_id']); } public function getCategory() { return $this->hasOne(PostMeta::className(), ['id' => 'post_meta_id']); } public function isCurrent() { return $this->user_id == Yii::$app->user->id; } public function beforeSave($insert) { if (parent::beforeSave($insert)) { return true; } else { return false; } } /** * @param bool $status * @return array|mixed */ public static function getStatuses($status = false) { $statuses = [ self::STATUS_DELETED => Yii::t('common', 'DELETED'), self::STATUS_ACTIVE => Yii::t('common', 'ACTIVE'), self::STATUS_EXCELLENT => Yii::t('common', 'EXCELLENT'), self::STATUS_TOP => Yii::t('common', 'TOP'), ]; return $status !== false ? ArrayHelper::getValue($statuses, $status) : $statuses; } } ================================================ FILE: common/models/PostComment.php ================================================ 1], ['comment', SpamValidator::className(), 'message' => '请勿发表垃圾内容'], [['ip'], 'string', 'max' => 255] ]; } public function getUser() { return $this->hasOne(User::className(), ['id' => 'user_id']); } public function getPost() { return $this->hasOne(Post::className(), ['id' => 'post_id']); } public function getTopic() { return $this->hasOne(Topic::className(), ['id' => 'post_id'])->where(['type' => 'topic']); } public function getLike() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'like', $this->id); } /** * 通过ID获取指定评论 * @param $id * @param string $condition * @return array|null|\yii\db\ActiveRecord|static * @throws NotFoundHttpException */ public static function findModel($id, $condition = '') { if (!$model = Yii::$app->cache->get('comment' . $id)) { $model = static::find() ->where(['id' => $id]) ->andWhere($condition) ->one(); } if ($model !== null) { Yii::$app->cache->set('comment' . $id, $model, 0); return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } /** * 通过ID获取指定评论 * @param $id * @return array|null|\yii\db\ActiveRecord|static * @throws NotFoundHttpException */ public static function findComment($id) { return static::findModel($id, ['status' => self::STATUS_ACTIVE]); } /** * 获取已经删除过的评论 * @param $id * @return array|null|\yii\db\ActiveRecord * @throws NotFoundHttpException */ public static function findDeletedComment($id) { return static::findModel($id, ['status' => self::STATUS_DELETED]); } /** * 评论列表 * @param $postId * @return Query */ public static function findCommentList($postId) { return static::find()->with('user')->where(['post_id' => $postId]); } /** * 自己写的评论 * @return bool */ public function isCurrent() { return $this->user_id == Yii::$app->user->id; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'parent' => '父级评论', 'post_id' => '文章ID', 'comment' => '评论', 'status' => '1为正常 0为禁用', 'user_id' => '用户ID', 'like_count' => '喜欢数', 'ip' => '评论者ip地址', 'created_at' => '创建时间', 'updated_at' => '修改时间', ]; } public $atUsers; public function beforeSave($insert) { if (!parent::beforeSave($insert)) { return false; } $this->comment = PostService::contentComment($this->comment, $this); return true; } public function afterSave($insert, $changedAttributes) { parent::afterSave($insert, $changedAttributes); $post = $this->topic; (new UserMeta())->saveNewMeta('topic', $this->post_id, 'follow'); (new NotificationService())->newReplyNotify(\Yii::$app->user->identity, $post, $this, $this->atUsers); // 更新回复时间 $post->lastCommentToUpdate(\Yii::$app->user->identity->username); if ($insert) { // 评论计数器 Topic::updateAllCounters(['comment_count' => 1], ['id' => $post->id]); // 更新个人总统计 UserInfo::updateAllCounters(['comment_count' => 1], ['user_id' => $this->user_id]); } \Yii::$app->cache->set('comment' . $this->id, $this, 0); } } ================================================ FILE: common/models/PostMeta.php ================================================ CacheableActiveRecord::className(), ]); } /** * @inheritdoc */ public function rules() { return [ [['count', 'order', 'created_at', 'updated_at', 'parent'], 'integer'], [['name'], 'string', 'max' => 100], [['alias', 'type'], 'string', 'max' => 32], [['description'], 'string', 'max' => 255], [['alias'], 'unique'] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'name' => '名称', 'parent' => '父级分类', 'alias' => '变量(别名)', 'type' => '项目类型', 'description' => '选项描述', 'count' => '项目所属内容个数', 'order' => '项目排序', 'created_at' => '创建时间', 'updated_at' => '修改时间', ]; } public static function blogCategory() { return ArrayHelper::map(static::find()->where(['type' => 'blog_category'])->all(), 'id', 'name'); } public static function topicCategory() { $parents = ArrayHelper::map( static::find()->where(['parent' => null])->orWhere(['parent' => 0])->orderBy(['order' => SORT_ASC])->all(), 'id', 'name' ); $nodes = []; foreach ($parents as $key => $value) { $nodes[$value] = ArrayHelper::map(static::find()->where(['parent' => $key])->asArray()->all(), 'id', 'name'); } return $nodes; } /** * 返回无人区节点id * @return PostMeta|int */ public static function noManLandId() { /** @var PostMeta $postMeta */ $postMeta = self::find()->where(['alias' => 'no-man-land'])->one(); if ($postMeta) { return $postMeta->id; } return $postMeta; } public function getChildren() { return $this->hasMany(self::className(), ['parent' => 'id'])->from(self::tableName() . ' p2'); } public function getTypes() { return [ 'topic_category' => '社区分类', 'blog_category' => '文章分类', ]; } /** * @param array $conditions * @return array */ public static function getNodesMap($conditions = []) { return ArrayHelper::map(static::find()->where(['type' => 'topic_category'])->andFilterWhere($conditions)->all(), 'alias', 'name'); } /** * @return array */ public function getParents() { return ArrayHelper::map(static::find()->where(['parent' => 0])->all(), 'id', 'name'); } /** * 获取父子节点 * @return array */ public static function getNodes() { $cacheKey = md5(__METHOD__); if (false === $nodes = \Yii::$app->cache->get($cacheKey)) { $parents = PostMeta::find()->where([PostMeta::tableName() . '.parent' => [0, null]])->joinWith('children')->orderBy(['order' => SORT_ASC])->all(); /*** @var PostMeta $parent */ foreach ($parents as $parent) { $nodes[$parent->alias] = $parent; } //一天缓存 \Yii::$app->cache->set($cacheKey, $nodes, 86400, new TagDependency([ 'tags' => [NamingHelper::getCommonTag(PostMeta::className())] ]) ); } return $nodes; } } ================================================ FILE: common/models/PostMetaSearch.php ================================================ $query, ]); $this->load($params); if (!$this->validate()) { // uncomment the following line if you do not want to any records when validation fails // $query->where('0=1'); return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'count' => $this->count, 'order' => $this->order, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'name', $this->name]) ->andFilterWhere(['like', 'alias', $this->alias]) ->andFilterWhere(['like', 'type', $this->type]) ->andFilterWhere(['like', 'description', $this->description]); return $dataProvider; } } ================================================ FILE: common/models/PostSearch.php ================================================ with('user', 'category'); // 如果有无人区节点 帖子列表过滤无人区节点的帖子 if (PostMeta::noManLandId() && (empty($params['PostSearch']['post_meta_id']) || $params['PostSearch']['post_meta_id'] != PostMeta::noManLandId())) { $query->andWhere(['!=', 'post_meta_id', PostMeta::noManLandId()]); } $dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => [ 'pageSize' => 20, ], 'sort' => ['defaultOrder' => [ 'order' => SORT_ASC, 'last_comment_time' => SORT_DESC, 'created_at' => SORT_DESC, ]] ]); if (!($this->load($params) && $this->validate())) { return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'post_meta_id' => $this->post_meta_id, 'user_id' => $this->user_id, 'view_count' => $this->view_count, 'comment_count' => $this->comment_count, 'favorite_count' => $this->favorite_count, 'like_count' => $this->like_count, 'thanks_count' => $this->thanks_count, 'hate_count' => $this->hate_count, 'status' => $this->status, 'order' => $this->order, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'type', $this->type]) ->andFilterWhere(['like', 'title', $this->title]) ->andFilterWhere(['like', 'author', $this->author]) ->andFilterWhere(['like', 'excerpt', $this->excerpt]) ->andFilterWhere(['like', 'image', $this->image]) ->andFilterWhere(['like', 'content', $this->content]) ->andFilterWhere(['like', 'tags', $this->tags]); return $dataProvider; } } ================================================ FILE: common/models/PostTag.php ================================================ 20] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'name' => '名称', 'count' => '计数', 'created_at' => '创建时间', 'updated_at' => '修改时间', ]; } } ================================================ FILE: common/models/PostTagSearch.php ================================================ $query, ]); $this->load($params); if (!$this->validate()) { // uncomment the following line if you do not want to any records when validation fails // $query->where('0=1'); return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'count' => $this->count, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'name', $this->name]); return $dataProvider; } } ================================================ FILE: common/models/RightLink.php ================================================ 255], [['url'], 'string', 'max' => 225], [['created_user'], 'string', 'max' => 32] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'title' => '名称', 'url' => 'Url', 'image' => '图片链接', 'content' => '内容', 'type' => '展示类别', 'created_user' => '创建人', 'created_at' => 'Created At', 'updated_at' => 'Updated At', ]; } /** * 分类 * @return array */ public function getTypes() { return [ '1' => '推荐资源', '2' => '小贴士', '3' => '友情链接', '4' => '首页提示语', ]; } } ================================================ FILE: common/models/Search.php ================================================ where($keyword)->andWhere(['status' => [1, 2]]) ->asArray() ->offset(0) ->limit(1000) ->all(); } } ================================================ FILE: common/models/SearchLog.php ================================================ 255] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'user_id' => Yii::t('app', '用户ID'), 'username' => Yii::t('app', '用户名'), 'keyword' => Yii::t('app', '搜索关键词'), 'created_at' => Yii::t('app', '创建时间'), ]; } public function getUser() { return $this->hasOne(User::className(), ['id' => 'user_id']); } } ================================================ FILE: common/models/Session.php ================================================ 40] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'expire' => Yii::t('app', 'Expire'), 'data' => Yii::t('app', 'Data'), ]; } } ================================================ FILE: common/models/User.php ================================================ [ 'class' => \DevGroup\TagDependencyHelper\CacheableActiveRecord::className(), ], ]; } /** * @inheritdoc */ public function rules() { return [ ['status', 'default', 'value' => self::STATUS_ACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]], ['role', 'default', 'value' => 10], ['role', 'in', 'range' => [self::ROLE_USER, self::ROLE_ADMIN, self::ROLE_SUPER_ADMIN]], ]; } /** * @inheritdoc */ public static function findIdentity($id) { return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); } /** * @inheritdoc */ public static function findIdentityByAccessToken($token, $type = null) { throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); } /** * Finds user by username * * @param string $username * @return static|null */ public static function findByUsername($username) { return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]); } /** * 邮箱登录 * @user onyony * @param $email * @return null|static */ public static function findByEmail($email) { return static::findOne(['email' => $email, 'status' => self::STATUS_ACTIVE]); } /** * Finds user by password reset token * * @param string $token password reset token * @return static|null */ public static function findByPasswordResetToken($token) { if (!static::isPasswordResetTokenValid($token)) { return null; } return static::findOne([ 'password_reset_token' => $token, 'status' => self::STATUS_ACTIVE, ]); } /** * Finds out if password reset token is valid * * @param string $token password reset token * @return boolean */ public static function isPasswordResetTokenValid($token) { if (empty($token)) { return false; } $expire = Yii::$app->params['user.passwordResetTokenExpire']; $parts = explode('_', $token); $timestamp = (int)end($parts); return $timestamp + $expire >= time(); } /** * @inheritdoc */ public function getId() { return $this->getPrimaryKey(); } /** * @inheritdoc */ public function getAuthKey() { return $this->auth_key; } /** * @inheritdoc */ public function validateAuthKey($authKey) { return $this->getAuthKey() === $authKey; } /** * Validates password * * @param string $password password to validate * @return boolean if password provided is valid for current user */ public function validatePassword($password) { return Yii::$app->security->validatePassword($password, $this->password_hash); } /** * Generates password hash from password and sets it to the model * * @param string $password */ public function setPassword($password) { $this->password_hash = Yii::$app->security->generatePasswordHash($password); } /** * Generates "remember me" authentication key */ public function generateAuthKey() { $this->auth_key = Yii::$app->security->generateRandomString(); } /** * Generates new password reset token */ public function generatePasswordResetToken() { $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); } /** * Removes password reset token */ public function removePasswordResetToken() { $this->password_reset_token = null; } /** * 获取用户头像 * @param int $size * @return string * @throws \yii\base\Exception */ public function getUserAvatar($size = 50) { if ($this->avatar) { // TODO 写法更优雅 $avatarPath = Yii::$app->basePath . Yii::$app->params['avatarPath']; $avatarCachePath = Yii::$app->basePath . Yii::$app->params['avatarCachePath']; FileHelper::createDirectory($avatarCachePath); // 创建文件夹 if (file_exists($avatarCachePath . $size . '_' . $this->avatar)) { // 缓存头像是否存在 return Yii::$app->params['avatarCacheUrl'] . $size . '_' . $this->avatar; } if (file_exists($avatarPath . $this->avatar)) { // 原始头像是否存在 \yii\imagine\Image::thumbnail($avatarPath . $this->avatar, $size, $size) ->save($avatarCachePath . $size . '_' . $this->avatar, ['quality' => 100]); return Yii::$app->params['avatarCacheUrl'] . $size . '_' . $this->avatar; } } return (new Avatar($this->email, $size))->getAvater(); } public function getUserInfo() { return $this->hasOne(UserInfo::className(), ['user_id' => 'id']); } public function getMerit() { return $this->hasOne(Merit::className(), ['user_id' => 'id']); } /** * @return array */ public function getAccounts() { $connected = []; $accounts = $this->hasMany(UserAccount::className(), ['user_id' => 'id'])->all(); // @var Account $account foreach ($accounts as $account) { $connected[$account->provider] = $account; } return $connected; } /** @inheritdoc */ public function afterSave($insert, $changedAttributes) { if ($insert) { $time = time(); $ip = isset(Yii::$app->request->userIP) ? Yii::$app->request->userIP : '127.0.0.1'; $userInfo = Yii::createObject([ 'class' => UserInfo::className(), 'user_id' => $this->id, 'prev_login_time' => $time, 'prev_login_ip' => $ip, 'last_login_time' => $time, 'last_login_ip' => $ip, 'created_at' => $time, 'updated_at' => $time, ]); $userInfo->save(); } parent::afterSave($insert, $changedAttributes); } public static function isAdmin($username) { if (static::findOne(['username' => $username, 'role' => self::ROLE_ADMIN])) { return true; } else { return false; } } public static function isSuperAdmin($username) { if (static::findOne(['username' => $username, 'role' => self::ROLE_SUPER_ADMIN])) { return true; } else { return false; } } /** * @return bool */ public static function currUserIsSuperAdmin() { return user()->identity && Yii::$app->user->identity->role == self::ROLE_SUPER_ADMIN; } /** * 获取权限 * @param $username * @return bool */ public static function getThrones($username = '') { if (!$username && Yii::$app->user->id) { $username = Yii::$app->user->identity->username; } else { return false; } if ($isAdmin = self::isAdmin($username)) { return $isAdmin; } return self::isSuperAdmin($username); } public static function getRole($role) { $data = [ self::ROLE_ADMIN => [ 'name' => '高级会员', 'color' => 'primary', ], self::ROLE_USER => [ 'name' => '会员', 'color' => 'info', ], self::ROLE_SUPER_ADMIN => [ 'name' => '管理员', 'color' => 'success', ] ]; return $data[$role]; } public static function getRoleList() { return [ self::ROLE_ADMIN => '高级会员', self::ROLE_USER => '会员', self::ROLE_SUPER_ADMIN => '管理员', ]; } public static function getStatus($status) { $data = [ self::STATUS_DELETED => [ 'name' => '已删除', 'color' => 'danger', ], self::STATUS_ACTIVE => [ 'name' => '正常', 'color' => 'default', ], ]; return $data[$status]; } public static function getStatusList() { return [ self::STATUS_DELETED => '已删除', self::STATUS_ACTIVE => '正常', ]; } } ================================================ FILE: common/models/UserInfo.php ================================================ 255], [['github', 'website'], 'string', 'max' => 100], [['company'], 'string', 'max' => 40], [['location'], 'string', 'max' => 10], [['prev_login_ip', 'last_login_ip'], 'string', 'max' => 32] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'user_id' => 'User ID', 'info' => '会员简介', 'github' => 'GitHub 帐号', 'website' => '个人主页', 'company' => '公司', 'location' => '城市', 'view_count' => '个人主页浏览次数', 'comment_count' => '发布评论数', 'post_count' => '发布文章数', 'thanks_count' => '被感谢次数', 'like_count' => '被赞次数', 'hate_count' => '喝倒彩次数', 'login_count' => '登录次数', 'prev_login_time' => '上次登录时间', 'prev_login_ip' => '上次登录IP', 'last_login_time' => '最后登录时间', 'last_login_ip' => '最后登录IP', 'created_at' => 'Created At', 'updated_at' => 'Updated At', ]; } } ================================================ FILE: common/models/UserSearch.php ================================================ $query, ]); $this->load($params); if (!$this->validate()) { // uncomment the following line if you do not want to any records when validation fails // $query->where('0=1'); return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'role' => $this->role, 'status' => $this->status, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'username', $this->username]) ->andFilterWhere(['like', 'avatar', $this->avatar]) ->andFilterWhere(['like', 'auth_key', $this->auth_key]) ->andFilterWhere(['like', 'password_hash', $this->password_hash]) ->andFilterWhere(['like', 'password_reset_token', $this->password_reset_token]) ->andFilterWhere(['like', 'email', $this->email]) ->andFilterWhere(['like', 'tagline', $this->tagline]); return $dataProvider; } } ================================================ FILE: common/services/CommentService.php ================================================ * createTime : 15/4/19 下午3:20 * description: */ namespace common\services; use common\models\PostComment; class CommentService { public function userDoAction($id, $action) { $comment = PostComment::findComment($id); $user = \Yii::$app->user->getIdentity(); if (in_array($action, ['like'])) { return UserService::CommentAction($user, $comment, $action); } } /** * 过滤内容 */ public function filterSame($content) { $content = strtolower($content); $content = trim($content); $data = ['test', '测试']; if (in_array($content, $data)) { return false; } $action = ['+1', '赞', '很赞' , '喜欢', '收藏', 'mark', '写的不错', '不错', '给力', '顶', '沙发', '前排', '留名', '路过']; if (in_array($content, $action)) { return false; } return true; } } ================================================ FILE: common/services/NotificationService.php ================================================ * createTime : 2015/4/21 16:56 * description: */ namespace common\services; use common\models\PostComment; use common\models\User; use frontend\models\Notification; use frontend\modules\topic\models\Topic; use common\models\Post; use frontend\modules\user\models\UserMeta; use yii\base\Exception; use yii\helpers\VarDumper; class NotificationService { public $notifiedUsers = []; /** * 评论和@用户会有通知 * @param User $fromUser * @param Topic $topic * @param PostComment $comment * @param $atUsers */ public function newReplyNotify(User $fromUser, Topic $topic, PostComment $comment, $atUsers) { $users = []; foreach ($topic->follower as $key => $value) { $users[$value->user_id] = $value->user_id; } // Notify mentioned users if (!$this->batchNotify('at', $fromUser, $atUsers, $topic, $comment)) { // 通知关注的用户 $this->batchNotify('new_comment', $fromUser, $users, $topic, $comment); } } /** * 内容@用户会有通知 * @param User $fromUser * @param Post $post * @param [] $users * @throws Exception */ public function newPostNotify(User $fromUser, Post $post, $users) { $this->batchNotify('at_' . $post->type, $fromUser, $users, $post); } /** * 点赞和其他动作通知 * @param $type * @param $fromUserId * @param $toUserId * @param Post $post * @param PostComment $comment * @throws Exception */ public function newActionNotify($type, $fromUserId, $toUserId, Post $post, PostComment $comment = null) { $model = new Notification(); $model->setAttributes([ 'from_user_id' => $fromUserId, 'user_id' => $toUserId, 'post_id' => $post->id, 'comment_id' => $comment ? $comment->id : 0, 'data' => $comment ? $comment->comment : $post->content, 'type' => $type, ]); if ($model->save()) { User::updateAllCounters(['notification_count' => 1], ['id' => $toUserId]); } else { throw new Exception(array_values($model->getFirstErrors())[0]); } } /** * 批量处理通知 * @param $type * @param User $fromUser * @param $users * @param Post $post * @param PostComment $comment * @return bool * @throws Exception */ public function batchNotify($type, User $fromUser, $users, Post $post, PostComment $comment = null) { foreach ($users as $key => $value) { if ($fromUser->id == $key) { continue; } $model = new Notification(); $model->setAttributes([ 'from_user_id' => $fromUser->id, 'user_id' => $key, 'post_id' => $post->id, 'comment_id' => $comment ? $comment->id : 0, 'data' => self::getNotifyData($type, $comment ? $comment->comment : $post->content), 'type' => $type, ]); $this->notifiedUsers[] = $key; if ($model->save()) { User::updateAllCounters(['notification_count' => 1], ['id' => $key]); } else { throw new Exception(array_values($model->getFirstErrors())[0]); } } return count($this->notifiedUsers); } /** * 查找用户的动作通知 * @param UserMeta $meta * @return null|Notification */ public function findUserActionNotify(UserMeta $meta) { if ($meta->target_type == 'comment') { $condition['comment_id'] = $meta->target_id; } else { $condition['post_id'] = $meta->target_id; } return Notification::findOne([ 'from_user_id' => $meta->user_id, 'type' => $meta->target_type . '_' . $meta->type, ] + $condition); } /** * @param $type * @param $data * @return string */ public static function getNotifyData($type, $data) { if (in_array($type, ['topic_like', 'topic_favorite', 'topic_thanks', 'at_topic'])) { return ''; } return $data; } } ================================================ FILE: common/services/PostService.php ================================================ * createTime : 15/4/19 下午3:20 * description: */ namespace common\services; use common\models\PostMeta; use common\models\PostSearch; use common\models\User; use common\models\Post; use DevGroup\TagDependencyHelper\NamingHelper; use frontend\models\Notification; use Yii; use yii\caching\TagDependency; use yii\helpers\ArrayHelper; use yii\helpers\Url; class PostService { /** * 删除帖子 * @param Post $post */ public static function delete(Post $post) { $post->setAttributes(['status' => Post::STATUS_DELETED]); $post->save(); Notification::updateAll(['status' => Post::STATUS_DELETED], ['post_id' => $post->id]); } /** * 过滤内容 * @param $content * @return bool */ public function filterContent($content) { $content = strtolower($content); $content = trim($content); $data = ['test', '测试']; if (in_array($content, $data)) { return false; } // $action = ['+1', '赞', '很赞', '喜欢', '收藏', 'mark', '写的不错', '不错', '给力']; // if (in_array($content, $action)) { // return false; // } return true; } public static function contentTopic($content, $model) { $content = static::contentReplaceAtUser($content, $model); return $content; } public static function contentComment($content, $model) { $content = static::contentReplaceAtUser($content, $model); $content = static::contentReplaceFloor($content); return $content; } public static function contentTweet($content, $model) { $content = static::contentReplaceAtUser($content, $model); $content = static::contentReplaceTag($content); return $content; } /** * 评论内容包含 '#n楼' 的将其替换为楼层锚链接 * @param $content string * @return string */ public static function contentReplaceFloor($content) { return preg_replace('/#(\d+)楼/', '[\0](#comment\1)', $content); } /** * 内容包含 '@summer ' 的将其替换为用户主页链接 * @param $content string * @return string */ public static function contentReplaceAtUser($content, $model) { $model->atUsers = $usernames = static::parseUsername($content); foreach ($usernames as $username) { $content = str_replace("@$username", sprintf('[@%s](%s)', $username, Url::to(['/user/default/show', 'username' => $username])), $content); } return $content; } public static function parseUsername($content) { preg_match_all('/@(\S{4,255}) /', $content, $matches); if (empty($matches[1])) { return []; } $existUserRows = User::find()->where(['username' => $matches[1]])->select('id,username')->asArray()->all(); return ArrayHelper::map($existUserRows, 'id', 'username') ?: []; } public static function contentReplaceTag($content) { $content = preg_replace_callback('/#(\S+?)#/', function ($matches) { $tagName = $matches[1]; return sprintf('[%s](%s)', "#$tagName#", Url::to(['/tweet/default/index', 'topic' => $tagName])); }, $content); return $content; } /** * @param $params * @return array */ public static function search($params) { $searchModel = new PostSearch(); // 话题或者分类筛选 empty($params['tag']) ?: $params['PostSearch']['tags'] = $params['tag']; if (isset($params['node'])) { $postMeta = PostMeta::findOne(['alias' => $params['node']]); ($postMeta) ? $params['PostSearch']['post_meta_id'] = $postMeta->id : null; } if (isset($params['tab'])) { $postMeta = PostMeta::findOne(['alias' => $params['tab']]); ($postMeta) ? $params['PostSearch']['post_meta_id'] = ArrayHelper::getColumn($postMeta->children, 'id') : null; } $dataProvider = $searchModel->search($params); $dataProvider->query->andWhere([Post::tableName() . '.type' => 'topic', 'status' => [Post::STATUS_ACTIVE, Post::STATUS_EXCELLENT]]); // 排序 $sort = $dataProvider->getSort(); $sort->attributes = array_merge($sort->attributes, [ 'hotest' => [ 'asc' => [ 'view_count' => SORT_DESC, 'created_at' => SORT_DESC ], ], 'excellent' => [ 'asc' => [ 'status' => SORT_DESC, 'view_count' => SORT_DESC, 'created_at' => SORT_DESC ], ], 'uncommented' => [ 'asc' => [ 'comment_count' => SORT_ASC, 'created_at' => SORT_DESC ], ] ]); return ['searchModel' => $searchModel, 'dataProvider' => $dataProvider]; } } ================================================ FILE: common/services/TopicService.php ================================================ * createTime : 15/4/19 下午3:20 * description: */ namespace common\services; use common\models\User; use frontend\modules\topic\models\Topic; class TopicService extends PostService { public function userDoAction($id, $action) { $topic = Topic::findTopic($id); /** @var User $user */ $user = \Yii::$app->user->getIdentity(); if (in_array($action, ['like', 'hate'])) { return UserService::TopicActionA($user, $topic, $action); } else { return UserService::TopicActionB($user, $topic, $action); } } /** * 撤销帖子 * @param Topic $topic */ public static function revoke(Topic $topic) { $topic->setAttributes(['status' => Topic::STATUS_ACTIVE]); $topic->save(); } /** * 加精华 * @param Topic $topic */ public static function excellent(Topic $topic) { $action = ($topic->status == Topic::STATUS_ACTIVE) ? Topic::STATUS_EXCELLENT : Topic::STATUS_ACTIVE; $topic->setAttributes(['status' => $action]); $topic->save(); } } ================================================ FILE: common/services/TweetService.php ================================================ * createTime : 15/4/19 下午3:20 * description: */ namespace common\services; use frontend\modules\topic\models\Topic; use frontend\modules\tweet\models\Tweet; use yii\helpers\Url; class TweetService extends PostService { public function userDoAction($id, $action) { $topic = Tweet::findTweet($id); $user = \Yii::$app->user->getIdentity(); return UserService::TopicActionB($user, $topic, $action); } /** * 撤销帖子 * @param Topic $topic */ public static function revoke(Topic $topic) { $topic->setAttributes(['status' => Topic::STATUS_ACTIVE]); $topic->save(); } /** * 加精华 * @param Topic $topic */ public static function excellent(Topic $topic) { $action = ($topic->status == Topic::STATUS_ACTIVE) ? Topic::STATUS_EXCELLENT : Topic::STATUS_ACTIVE; $topic->setAttributes(['status' => $action]); $topic->save(); } } ================================================ FILE: common/services/UserService.php ================================================ * createTime : 15/4/19 下午3:20 * description: */ namespace common\services; use common\models\Post; use common\models\PostComment; use common\models\User; use common\models\UserInfo; use DevGroup\TagDependencyHelper\NamingHelper; use frontend\modules\topic\models\Topic; use frontend\modules\user\models\UserMeta; use yii\caching\TagDependency; class UserService { /** * 获取通知条数 * @return mixed */ public static function findNotifyCount() { $user = \Yii::$app->getUser()->getIdentity(); return $user ? $user->notification_count : null; } /** * 清除通知数 * @return mixed */ public static function clearNotifyCount() { return User::updateAll(['notification_count' => '0'], ['id' => \Yii::$app->user->id]); } /** * 赞话题(如果已经赞,则取消赞) * @param User $user * @param Topic $topic * @param $action 动作 * @return array */ public static function TopicActionA(User $user, Topic $topic, $action) { return self::toggleType($user, $topic, $action); } /** * 用户对话题其他动作 * @param User $user * @param Post $model * @param $action fa * @return array */ public static function TopicActionB(User $user, Post $model, $action) { $data = [ 'target_id' => $model->id, 'target_type' => $model->type, 'user_id' => $user->id, 'value' => '1', ]; if (!UserMeta::deleteOne($data + ['type' => $action])) { // 删除数据有行数则代表有数据,无行数则添加数据 $userMeta = new UserMeta(); $userMeta->setAttributes($data + ['type' => $action]); $result = $userMeta->save(); if ($result) { $model->updateCounters([$action . '_count' => 1]); if ($action == 'thanks') { UserInfo::updateAllCounters([$action . '_count' => 1], ['user_id' => $model->user_id]); } } return [$result, $userMeta]; } $model->updateCounters([$action . '_count' => -1]); if ($action == 'thanks') { UserInfo::updateAllCounters([$action . '_count' => -1], ['user_id' => $model->user_id]); } return [true, null]; } /** * 对评论点赞 * @param User $user * @param PostComment $comment * @param $action * @return array */ public static function CommentAction(User $user, PostComment $comment, $action) { $data = [ 'target_id' => $comment->id, 'target_type' => $comment::TYPE, 'user_id' => $user->id, 'value' => '1', ]; if (!UserMeta::deleteOne($data + ['type' => $action])) { // 删除数据有行数则代表有数据,无行数则添加数据 $userMeta = new UserMeta(); $userMeta->setAttributes($data + ['type' => $action]); $result = $userMeta->save(); if ($result) { $comment->updateCounters([$action . '_count' => 1]); // 更新个人总统计 UserInfo::updateAllCounters([$action . '_count' => 1], ['user_id' => $comment->user_id]); } return [$result, $userMeta]; } $comment->updateCounters([$action . '_count' => -1]); // 更新个人总统计 UserInfo::updateAllCounters([$action . '_count' => -1], ['user_id' => $comment->user_id]); return [true, null]; } /** * 喝倒彩或者赞 * @param User $user * @param Post $model * @param $action 动作 * @return array */ protected static function toggleType(User $user, Post $model, $action) { $data = [ 'target_id' => $model->id, 'target_type' => $model->type, 'user_id' => $user->id, 'value' => '1', ]; if (!UserMeta::deleteOne($data + ['type' => $action])) { // 删除数据有行数则代表有数据,无行数则添加数据 $userMeta = new UserMeta(); $userMeta->setAttributes($data + ['type' => $action]); $result = $userMeta->save(); if ($result) { // 如果是新增数据, 删除掉Hate的同类型数据 $attributeName = ($action == 'like' ? 'hate' : 'like'); $attributes = [$action . '_count' => 1]; if (UserMeta::deleteOne($data + ['type' => $attributeName])) { // 如果有删除hate数据, hate_count也要-1 $attributes[$attributeName . '_count'] = -1; } //更新版块统计 $model->updateCounters($attributes); // 更新个人总统计 UserInfo::updateAllCounters($attributes, ['user_id' => $model->user_id]); } return [$result, $userMeta]; } $model->updateCounters([$action . '_count' => -1]); UserInfo::updateAllCounters([$action . '_count' => -1], ['user_id' => $model->user_id]); return [true, null]; } /** * 查找活跃用户 * @param int $limit * @return array|\yii\db\ActiveRecord[] */ public static function findActiveUser($limit = 12) { $cacheKey = md5(__METHOD__ . $limit); if (false === $items = \Yii::$app->cache->get($cacheKey)) { $items = User::find() ->joinWith(['merit', 'userInfo']) ->where([User::tableName() . '.status' => 10]) ->orderBy(['merit' => SORT_DESC, '(like_count+thanks_count)' => SORT_DESC]) ->limit($limit) ->all(); //一天缓存 \Yii::$app->cache->set($cacheKey, $items, 86400, new TagDependency([ 'tags' => [NamingHelper::getCommonTag(User::className())] ]) ); } return $items; } } ================================================ FILE: common/widgets/JsBlock.php ================================================ renderInPlace) { throw new \Exception("not implemented yet ! "); // echo $block; } $block = trim($block); /* $jsBlockPattern = '|^]*>(.+?)$|is'; if(preg_match($jsBlockPattern,$block)){ $block = preg_replace ( $jsBlockPattern , '${1}' , $block ); } */ $jsBlockPattern = '|^]*>(?P.+?)$|is'; if (preg_match($jsBlockPattern, $block, $matches)) { $block = $matches['block_content']; } $this->view->registerJs($block, $this->pos, $this->key); } } ================================================ FILE: composer.json ================================================ { "name": "iiyii/getyii", "description": "Yii 2 Community", "keywords": [ "yii2", "framework", "advanced", "community", "application template" ], "homepage": "https://github.com/iiYii/getyii/", "type": "project", "license": "BSD-3-Clause", "support": { "issues": "https://github.com/iiYii/getyii/issues?state=open", "forum": "http://www.getyii.com/", "wiki": "https://github.com/iiYii/getyii/wiki/", "source": "https://github.com/iiYii/getyii" }, "minimum-stability": "stable", "require": { "php": ">=5.4.0", "yiisoft/yii2": ">=2.0.6", "yiisoft/yii2-bootstrap": "*", "yiisoft/yii2-swiftmailer": "*", "conquer/select2": "*", "bower-asset/highlightjs": "*", "bower-asset/pace": "*", "bower-asset/localforage": "*", "bower-asset/at.js": "*", "yiisoft/yii2-authclient": "*", "yzalis/identicon": "*", "funson86/yii2-setting": "*", "yiisoft/yii2-imagine": "^2.0", "hightman/xunsearch": "*@beta", "yiier/yii2-backup": "*", "kop/yii2-scroll-pager": "dev-master", "devgroup/yii2-tag-dependency-helper": "*", "yiier/yii2-return-url": "*", "yiier/yii2-merit": "*", "yiisoft/yii2-httpclient": "dev-master", "understeam/yii2-slack": "~0.1", "dmstr/yii2-adminlte-asset": "2.*", "yiier/yii2-editor.md": "*", "2amigos/qrcode-library": "^1.1", "yiier/yii2-anti-spam": "^0.2.0", "ext-curl": "*" }, "require-dev": { "fxp/composer-asset-plugin": "^1.2.0", "yiisoft/yii2-codeception": "*", "yiisoft/yii2-debug": "*", "yiisoft/yii2-gii": "*", "yiisoft/yii2-faker": "*" }, "config": { "process-timeout": 1800, "fxp-asset": { "enabled": false } }, "autoload": { "psr-4": { "common\\": "common" }, "files": [ "common/components/GlobalFunctions.php" ] }, "scripts": { "post-create-project-cmd": [ "yii\\composer\\Installer::postCreateProject" ], "post-install-cmd": [ "common\\components\\ComposerInstaller::initProject" ], "post-update-cmd": [ "common\\components\\ComposerInstaller::initProject" ] }, "repositories": [ { "type": "composer", "url": "https://asset-packagist.org" } ] } ================================================ FILE: console/config/.gitignore ================================================ main-local.php params-local.php ================================================ FILE: console/config/bootstrap.php ================================================ 'app-console', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log', 'gii'], 'controllerNamespace' => 'console\controllers', 'modules' => [ 'gii' => 'yii\gii\Module', ], 'components' => [ 'log' => [ 'targets' => [ [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning', 'info'], ], ], ], 'user' => [ 'class' => 'yii\web\User', 'identityClass' => 'common\models\User', //'enableAutoLogin' => true, ], ], 'params' => $params, 'controllerMap' => [ 'backup' => [ 'class' => 'yiier\backup\controllers\BackupController', ] ] ]; ================================================ FILE: console/config/params.php ================================================ 'admin@example.com', ]; ================================================ FILE: console/controllers/.gitkeep ================================================ ================================================ FILE: console/controllers/InstallController.php ================================================ ansiFormat("OK", Console::FG_GREEN), $content); $content = str_replace('WARNING!!!', $this->ansiFormat("WARNING!!!", Console::FG_YELLOW), $content); $content = str_replace('FAILED!!!', $this->ansiFormat("FAILED!!!", Console::FG_RED), $content); $this->stdout($content); } /** * 项目安装 当代码第一次初始化后执行此命令可引导安装项目 */ public function actionIndex() { $lockFile = Yii::getAlias('@root/install.lock'); if (!file_exists($lockFile)) { $result = $this->runSteps([ '数据库配置' => 'db', '初始化数据库数据' => 'migrate' ]); if ($result) { $this->stdout("恭喜, 站点配置成功!\n", Console::FG_GREEN); touch($lockFile); } } else { $this->stdout("站点已经配置完毕,无需再配置\n", Console::FG_GREEN); $this->stdout(" - 如需重新配置, 请删除{$lockFile}文件后再执行命令!\n"); } } public function runSteps(array $steps) { $i = 1; foreach ($steps as $step => $args) { $this->stdout("\n\n - Step {$i} {$step} \n"); $this->stdout("==================================================\n"); !is_array($args) && $args = (array)$args; $method = array_shift($args); $result = call_user_func_array([$this, 'action' . $method], $args); if ($result === false) { $this->stdout("{$step}失败, 退出安装流程\n", Console::FG_RED); return false; } $i++; } return true; } /** * 生成数据库配置文件 * @return mixed */ public function actionDb() { $dbFile = Yii::getAlias('@common/config/db-local.php'); if (!file_exists($dbFile)) { $this->stdout("默认数据库配置文件未找到,将进入数据库配置创建流程\n", Console::FG_RED); $result = $this->generateDbFile($dbFile); if ($result !== false) { // 生成文件了之后.加载db配置 Yii::$app->set('db', require $dbFile); } return $result; } $this->stdout("'{$dbFile}' 配置文件已存在, 无需配置\n"); } /** * 创建数据库配置文件 * @param $dbFile * @return mixed */ public function generateDbFile($dbFile) { $host = $this->prompt('请输入数据库主机地址:', [ 'default' => 'localhost' ]); $dbName = $this->prompt('请输入数据库名称:', [ 'default' => 'getyii' ]); $dbConfig = [ 'dsn' => "mysql:host={$host};dbname={$dbName}", 'username' => $this->prompt("请输入数据库访问账号:", [ 'default' => 'root' ]), 'password' => $this->prompt("请输入数据库访问密码:"), 'tablePrefix' => $this->prompt("请输入数据库表前缀(默认不使用):", [ 'default' => '' ]), 'charset' => $this->prompt("请输入数据默认的字符集:", [ 'default' => 'utf8mb4' ]) ]; $message = null; if ($this->confirm('是否测试数据库可用?', true)) { $db = Yii::createObject(array_merge([ 'class' => 'yii\db\Connection' ], $dbConfig)); try { $db->open(); $this->stdout("数据连接成功\n", Console::FG_GREEN); } catch (\Exception $e) { $this->stdout("数据连接失败:" . $e->getMessage() . "\n", Console::FG_RED); $message = '依然写入文件?(如果依然写入文件, 会影响后续安装步骤)'; } } if ($message === null || $this->confirm($message)) { $this->stdout("生成数据库配置文件...\n"); $code = << 'yii\db\Connection', 'dsn' => '{$dbConfig['dsn']}', 'username' => '{$dbConfig['username']}', 'password' => '{$dbConfig['password']}', 'tablePrefix' => '{$dbConfig['tablePrefix']}', 'charset' => '{$dbConfig['charset']}', ]; EOF; file_put_contents($dbFile, $code); $this->stdout("恭喜! 数据库配置完毕!\n", Console::FG_GREEN); } elseif ($this->confirm("是否重新设置?", true)) { return $this->generateDbFile($dbFile); } else { return false; } } /** * 生成数据库结构和数据 */ public function actionMigrate() { $this->stdout("\n开始迁移数据库结构和数据\n", Console::FG_GREEN); $this->stdout("** 如无特殊需求,当询问是否迁移数据是回复yes既可 **\n", Console::FG_RED); // 默认迁移目录 $migrationsPath = array( '默认目录' => Yii::getAlias('@console/migrations') ); foreach ($migrationsPath as $name => $migrationPath) { if (!is_dir($migrationPath)) { continue; } $this->stdout("\n\n{$name}迁移: {$migrationPath}\n", Console::FG_YELLOW); Yii::$app->runAction('migrate/up', [ 'migrationPath' => $migrationPath ]); } } } ================================================ FILE: console/controllers/SyncController.php ================================================ 0, 'like_count' => 0, 'hate_count' => 0]); $meta = UserMeta::find()->all(); foreach ($meta as $key => $value) { if (in_array($value->type, ['thanks', 'like', 'hate'])) { switch ($value->target_type) { case 'topic': case 'post': $this->stdout("同步文章操作……\n"); $topic = Topic::findOne($value->target_id); if (UserInfo::updateAllCounters([$value->type . '_count' => 1], ['user_id' => $topic->user_id])) { $this->stdout("同步评论成功`(*∩_∩*)′\n"); } else { $this->stdout("同步评论失败::>_<::\n"); } break; case 'comment': $this->stdout("同步评论操作……\n"); $comment = PostComment::findOne($value->target_id); if (UserInfo::updateAllCounters([$value->type . '_count' => 1], ['user_id' => $comment->user_id])) { $this->stdout("同步评论成功`(*∩_∩*)′\n"); } else { $this->stdout("同步评论失败::>_<:: \n"); } break; default: # code... break; } } } return; } public function actionPost() { $update = Topic::updateAll( ['last_comment_time' => new Expression('created_at')], // ['or', ['type' => Topic::TYPE, 'last_comment_username' => ''], ['type' => Topic::TYPE, 'last_comment_username' => null]] ['and', ['type' => Topic::TYPE], ['or', ['last_comment_username' => ''], ['last_comment_username' => null]]] ); $this->stdout("同步最后回复时间,同步{$update}条数据\n"); $subQuery = new Query(); $subQuery->from(PostComment::tableName())->where(['status' => PostComment::STATUS_ACTIVE])->orderBy(['created_at' => SORT_DESC]); $comment = PostComment::find()->from(['tmpA' => $subQuery]) ->groupBy('post_id') ->all(); Topic::updateAll(['comment_count' => 0], ['type' => Topic::TYPE]); $updateComment = []; foreach ($comment as $value) { $commentCount = PostComment::find()->where(['post_id' => $value->post_id, 'status' => PostComment::STATUS_ACTIVE])->count(); $updateComment[] = Topic::updateAll( [ 'last_comment_time' => $value->created_at, 'last_comment_username' => $value->user->username, 'comment_count' => $commentCount, ], ['id' => $value->post_id, 'type' => Topic::TYPE] ); } $this->stdout("校正最后回复时间和回复会员还有评论条数,校正" . count($updateComment) . "条数据\n"); } } ================================================ FILE: console/migrations/m130524_201442_init.php ================================================ createTable('{{%user}}', [ 'id' => Schema::TYPE_PK, 'username' => Schema::TYPE_STRING . ' NOT NULL', 'auth_key' => Schema::TYPE_STRING . '(32) NOT NULL', 'password_hash' => Schema::TYPE_STRING . ' NOT NULL', 'password_reset_token' => Schema::TYPE_STRING, 'email' => Schema::TYPE_STRING . ' NOT NULL', 'role' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $this->tableOptions); } public function down() { $this->dropTable('{{%user}}'); } } ================================================ FILE: console/migrations/m150104_071047_init_blog.php ================================================ createTable($tableName, [ 'id' => Schema::TYPE_PK, 'name' => Schema::TYPE_STRING . "(100) DEFAULT NULL COMMENT '名称'", 'type' => Schema::TYPE_STRING . "(32) DEFAULT NULL COMMENT '项目类型'", 'description' => Schema::TYPE_STRING . " DEFAULT NULL COMMENT '选项描述'", 'count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '项目所属内容个数'", 'order' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '项目排序'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'", ], $this->tableOptions); $this->createIndex('type', $tableName, 'type'); // 文章 $tableName = '{{%post}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'post_meta_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '版块ID'", 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '作者ID'", 'title' => Schema::TYPE_STRING . " NOT NULL COMMENT '标题'", 'author' => Schema::TYPE_STRING . "(100) DEFAULT NULL COMMENT '作者'", 'excerpt' => Schema::TYPE_STRING . " DEFAULT NULL COMMENT '摘要'", 'image' => Schema::TYPE_STRING . " DEFAULT NULL COMMENT '封面图片'", 'content' => Schema::TYPE_TEXT . " NOT NULL COMMENT '内容'", 'tags' => Schema::TYPE_STRING . "(30) NOT NULL COMMENT '标签 用英文逗号隔开'", 'view_count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '查看数'", 'comment_count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '评论数'", 'favorite_count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '收藏数'", 'like_count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '喜欢数'", 'thanks_count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '感谢数'", 'hate_count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '讨厌数'", 'status' => Schema::TYPE_BOOLEAN . " NOT NULL DEFAULT '1' COMMENT '状态 1:发布 0:草稿'", 'order' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '999' COMMENT '排序 0最大'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'", ]); $this->createIndex('post_meta_id', $tableName, 'post_meta_id'); $this->createIndex('tags', $tableName, 'tags'); $this->createIndex('user_id', $tableName, 'user_id'); // 标签表 $tableName = '{{%post_tag}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'name' => Schema::TYPE_STRING . "(20) DEFAULT NULL COMMENT '名称'", 'count' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '计数'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'", ], $this->tableOptions); // 评论表 $tableName = '{{%post_comment}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'parent' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '父级评论'", 'post_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '文章ID'", 'comment' => Schema::TYPE_TEXT . " NOT NULL COMMENT '评论'", 'status' => Schema::TYPE_BOOLEAN . " NOT NULL DEFAULT '1' COMMENT '1为正常 0为禁用'", 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '用户ID'", 'ip' => Schema::TYPE_STRING . " NOT NULL COMMENT '评论者ip地址'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('post_id', $tableName, 'post_id'); $this->createIndex('user_id', $tableName, 'user_id'); } public function down() { echo "m150104_071047_init_blog cannot be reverted.\n"; $this->dropTable('{{%post_meta}}'); $this->dropTable('{{%post}}'); $this->dropTable('{{%post_tag}}'); $this->dropTable('{{%post_comment}}'); return false; } } ================================================ FILE: console/migrations/m150104_091352_init_user.php ================================================ addColumn('{{%user}}', 'avatar' , Schema::TYPE_STRING . " DEFAULT NULL COMMENT '头像' AFTER `username` "); // 会员动作表 $tableName = '{{%user_meta}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '用户ID'", 'type' => Schema::TYPE_STRING . "(100) NOT NULL DEFAULT '' COMMENT '操作类型'", 'value' => Schema::TYPE_STRING . " NOT NULL DEFAULT '' COMMENT '操作类型值'", 'target_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '目标id'", 'target_type' => Schema::TYPE_STRING . "(100) NOT NULL DEFAULT '' COMMENT '目标类型'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('type', $tableName, 'type'); $this->createIndex('user_id', $tableName, 'user_id'); $this->createIndex('target_id', $tableName, 'target_id'); $this->createIndex('target_type', $tableName, 'target_type'); // 会员第三方登录表 $tableName = '{{%user_auth}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '用户ID'", 'type' => Schema::TYPE_STRING . "(100) NOT NULL DEFAULT '' COMMENT '联合登录类型'", 'token' => Schema::TYPE_STRING . " NOT NULL", 'openid' => Schema::TYPE_STRING . " NOT NULL", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('type', $tableName, 'type'); $this->createIndex('user_id', $tableName, 'user_id'); } public function down() { echo "m150104_091352_init_user cannot be reverted.\n"; $this->dropColumn('{{%user}}', 'avatar'); $this->dropTable('{{%user_meta}}'); $this->dropTable('{{%user_auth}}'); return false; } } ================================================ FILE: console/migrations/m150115_081356_create_user_info.php ================================================ createTable('{{%user_info}}', [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 'info' => Schema::TYPE_STRING . ' DEFAULT NULL COMMENT "会员简介"', 'login_count' => Schema::TYPE_INTEGER . ' DEFAULT 1 COMMENT "登录次数"', 'prev_login_time' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL COMMENT "上次登录时间"', 'prev_login_ip' => Schema::TYPE_STRING . '(32) NOT NULL COMMENT "上次登录IP"', 'last_login_time' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL COMMENT "最后登录时间"', 'last_login_ip' => Schema::TYPE_STRING . '(32) NOT NULL COMMENT "最后登录IP"', 'created_at' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', ], $this->tableOptions); } public function down() { echo "m150115_081356_create_user_info cannot be reverted.\n"; $this->dropTable('{{%user_info}}'); return false; } } ================================================ FILE: console/migrations/m150201_142415_update_user.php ================================================ addColumn('{{%user_info}}', 'location' , Schema::TYPE_STRING . '(10) DEFAULT NULL COMMENT "城市" AFTER `info`'); $this->addColumn('{{%user_info}}', 'company' , Schema::TYPE_STRING . '(40) DEFAULT NULL COMMENT "公司" AFTER `info`'); $this->addColumn('{{%user_info}}', 'website' , Schema::TYPE_STRING . '(100) DEFAULT NULL COMMENT "个人主页" AFTER `info`'); $this->addColumn('{{%user_info}}', 'github' , Schema::TYPE_STRING . '(100) DEFAULT NULL COMMENT "GitHub 帐号" AFTER `info`'); $this->addColumn('{{%user}}', 'tagline' , Schema::TYPE_STRING . '(40) DEFAULT NULL COMMENT "一句话介绍" AFTER `email`'); } public function down() { echo "m150201_142415_update_user cannot be reverted.\n"; $this->dropColumn('{{%user_info}}', 'location'); $this->dropColumn('{{%user_info}}', 'company'); $this->dropColumn('{{%user_info}}', 'website'); $this->dropColumn('{{%user_info}}', 'github'); $this->dropColumn('{{%user}}', 'tagline'); return false; } } ================================================ FILE: console/migrations/m150205_085033_update_post_comment.php ================================================ addColumn('{{%post_comment}}', 'updated_at', Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'"); $this->addColumn('{{%post_comment}}', 'like_count', Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '喜欢数' AFTER `user_id`"); } public function down() { echo "m150205_085033_update_post_comment cannot be reverted.\n"; $this->dropColumn('{{%post_comment}}', 'updated_at'); $this->dropColumn('{{%post_comment}}', 'like_count'); return false; } } ================================================ FILE: console/migrations/m150209_015931_setting_init.php ================================================ execute($this->delSetting()); $tableName = '{{%setting}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'parent_id' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0', 'code' => Schema::TYPE_STRING . '(32) NOT NULL', 'type' => Schema::TYPE_STRING . '(32) NOT NULL', 'store_range' => Schema::TYPE_STRING . '(255)', 'store_dir' => Schema::TYPE_STRING . '(255)', 'value' => Schema::TYPE_TEXT . '', 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 50', ], $this->tableOptions); // Indexes $this->createIndex('parent_id', $tableName, 'parent_id'); $this->createIndex('code', $tableName, 'code'); $this->createIndex('sort_order', $tableName, 'sort_order'); // Add default setting $this->execute($this->getSettingSql()); } /** * @return string SQL to insert first user */ private function getSettingSql() { return "INSERT INTO {{%setting}} (`id`, `parent_id`, `code`, `type`, `store_range`, `store_dir`, `value`, `sort_order`) VALUES (11, 0, 'info', 'group', '', '', '', '50'), (21, 0, 'basic', 'group', '', '', '', '50'), (31, 0, 'smtp', 'group', '', '', '', '50'), (41, 0, 'github', 'group', '', '', '', '50'), (51, 0, 'google', 'group', '', '', '', '50'), (1111, 11, 'siteName', 'text', '', '', 'Your Site', '50'), (1112, 11, 'siteTitle', 'text', '', '', 'Your Site Title', '50'), (1113, 11, 'siteKeyword', 'text', '', '', 'Your Site Keyword', '50'), (2111, 21, 'timezone', 'select', '-12,-11,-10,-9,-8,-7,-6,-5,-4,-3.5,-3,-2,-1,0,1,2,3,3.5,4,4.5,5,5.5,5.75,6,6.5,7,8,9,9.5,10,11,12', '', '8', '50'), (2112, 21, 'commentCheck', 'select', '0,1', '', '1', '50'), (3111, 31, 'smtpHost', 'text', '', '', 'localhost', '50'), (3112, 31, 'smtpPort', 'text', '', '', '', '50'), (3113, 31, 'smtpUser', 'text', '', '', '', '50'), (3114, 31, 'smtpPassword', 'password', '', '', '', '50'), (3115, 31, 'smtpMail', 'text', '', '', '', '50'), (4111, 41, 'githubLogin', 'select', '0,1', '', '1', '50'), (4112, 41, 'githubClientId', 'text', '', '', '', '50'), (4113, 41, 'githubClientSecret', 'text', '', '', '', '50'), (5111, 51, 'googleLogin', 'select', '0,1', '', '1', '50'), (5112, 51, 'googleClientId', 'text', '', '', '', '50'), (5113, 51, 'googleClientSecret', 'text', '', '', '', '50') "; } private function delSetting() { return "DROP TABLE IF EXISTS {{%setting}};"; } public function down() { echo "m150209_015931_setting_init cannot be reverted.\n"; $this->dropTable('{{%setting}}'); return false; } } ================================================ FILE: console/migrations/m150209_090406_create_user_account.php ================================================ execute($this->delTable()); // 会员第三方登录账号表 $tableName = '{{%user_account}}'; $this->createTable($tableName, [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '用户ID'", 'provider' => Schema::TYPE_STRING . "(100) NOT NULL DEFAULT '' COMMENT '授权提供商'", 'client_id' => Schema::TYPE_STRING . " NOT NULL", 'data' => Schema::TYPE_TEXT . " NOT NULL", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('client_id', $tableName, 'client_id'); $this->createIndex('user_id', $tableName, 'user_id'); } private function delTable() { return "DROP TABLE IF EXISTS {{%user_auth}};"; } public function down() { echo "m150209_090406_create_user_account cannot be reverted.\n"; $this->dropTable('{{%user_account}}'); return false; } } ================================================ FILE: console/migrations/m150211_070335_update_user_info.php ================================================ addColumn('{{%user_info}}', 'like_count' , Schema::TYPE_INTEGER . ' DEFAULT 0 COMMENT "被赞次数" AFTER `location`'); $this->addColumn('{{%user_info}}', 'thanks_count' , Schema::TYPE_INTEGER . ' DEFAULT 0 COMMENT "被感谢次数" AFTER `location`'); $this->addColumn('{{%user_info}}', 'post_count' , Schema::TYPE_INTEGER . ' DEFAULT 0 COMMENT "发布文章数" AFTER `location`'); $this->addColumn('{{%user_info}}', 'comment_count' , Schema::TYPE_INTEGER . ' DEFAULT 0 COMMENT "发布评论数" AFTER `location`'); $this->addColumn('{{%user_info}}', 'view_count' , Schema::TYPE_INTEGER . ' DEFAULT 0 COMMENT "个人主页浏览次数" AFTER `location`'); } public function down() { echo "m150211_070335_update_user_info cannot be reverted.\n"; $this->dropColumn('{{%user_info}}', 'like_count'); $this->dropColumn('{{%user_info}}', 'thanks_count'); $this->dropColumn('{{%user_info}}', 'post_count'); $this->dropColumn('{{%user_info}}', 'comment_count'); $this->dropColumn('{{%user_info}}', 'view_count'); return false; } } ================================================ FILE: console/migrations/m150212_030127_update_user_info_and_post_meta.php ================================================ addColumn('{{%user_info}}', 'hate_count' , Schema::TYPE_INTEGER . ' DEFAULT 0 COMMENT "喝倒彩次数" AFTER `like_count`'); } public function down() { echo "m150212_030127_update_user_info_and_post_meta cannot be reverted.\n"; $this->dropColumn('{{%user_info}}', 'hate_count'); return false; } } ================================================ FILE: console/migrations/m150212_132400_create_topics_table.php ================================================ addColumn('{{%post}}', 'type' , Schema::TYPE_STRING . '(32) DEFAULT "blog" COMMENT "内容类型" AFTER `id`'); // 修改字段 $this->alterColumn('{{%post}}', 'tags' , Schema::TYPE_STRING . " DEFAULT NULL COMMENT '标签 用英文逗号隔开'"); $this->alterColumn('{{%post}}', 'post_meta_id' , Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '版块ID'"); $this->createIndex('type', '{{%post}}', 'type'); } public function down() { echo "m150212_132400_create_topics_table cannot be reverted.\n"; $this->dropColumn('{{%post}}', 'type'); return false; } } ================================================ FILE: console/migrations/m150214_070754_update_post_meta.php ================================================ addColumn('{{%post_meta}}', 'alias' , Schema::TYPE_STRING . "(32) DEFAULT NULL COMMENT '变量(别名)' AFTER `name`"); $this->createIndex('alias', '{{%post_meta}}', 'alias'); } public function down() { echo "m150214_070754_update_post_meta cannot be reverted.\n"; $this->dropColumn('{{%post_meta}}', 'alias'); return false; } } ================================================ FILE: console/migrations/m150412_034147_update_site_setting.php ================================================ execute($this->delSettingSql()); $this->execute($this->updateSettingSql()); $this->execute($this->getSettingSql()); } /** * @return string SQL to insert first user */ private function getSettingSql() { return "INSERT INTO {{%setting}} (`id`, `parent_id`, `code`, `type`, `store_range`, `store_dir`, `value`, `sort_order`) VALUES (1114, 11, 'siteAnalytics', 'text', '', '', 'Your Site Analytics', '50'), (4114, 41, 'googleLogin', 'select', '0,1', '', '1', '50'), (4115, 41, 'googleClientId', 'text', '', '', '', '50'), (4116, 41, 'googleClientSecret', 'text', '', '', '', '50'), (4117, 41, 'weiboLogin', 'select', '0,1', '', '1', '50'), (4118, 41, 'weiboClientId', 'text', '', '', '', '50'), (4119, 41, 'weiboClientSecret', 'text', '', '', '', '50'), (4120, 41, 'qqLogin', 'select', '0,1', '', '1', '50'), (4121, 41, 'qqClientId', 'text', '', '', '', '50'), (4122, 41, 'qqClientSecret', 'text', '', '', '', '50') "; } private function updateSettingSql() { return "UPDATE {{%setting}} SET `code` = 'account' WHERE `id` = 41"; } private function delSettingSql() { return "DELETE FROM {{%setting}} WHERE `id` IN ( 51, 5111, 5112, 5113 )"; } } ================================================ FILE: console/migrations/m150416_134819_create_notification_table.php ================================================ createTable($tableName, [ 'id' => Schema::TYPE_PK, 'from_user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '通知来源用户ID'", 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '用户ID'", 'post_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL COMMENT '文章ID'", 'comment_id' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '文章评论ID'", 'type' => Schema::TYPE_STRING . " NOT NULL COMMENT '通知类型'", 'data' => Schema::TYPE_TEXT . " NOT NULL COMMENT '通知内容'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('type', $tableName, 'type'); $this->createIndex('post_id', $tableName, 'post_id'); $this->createIndex('user_id', $tableName, 'user_id'); $this->addColumn('{{%user}}', 'notification_count' , Schema::TYPE_INTEGER . ' UNSIGNED DEFAULT 0 COMMENT "通知条数" AFTER `tagline`'); } public function down() { echo "m150416_134819_create_notifications_table cannot be reverted.\n"; $this->dropTable('{{%notification}}'); $this->dropColumn('{{%user}}', 'notification_count'); return false; } } ================================================ FILE: console/migrations/m150420_060807_update_post_table.php ================================================ addColumn('{{%post}}', 'follow_count' , Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '讨厌数' AFTER `view_count`"); } public function down() { echo "m150420_060807_update_post_table cannot be reverted.\n"; $this->dropColumn('{{%post}}', 'follow_count'); return false; } } ================================================ FILE: console/migrations/m150424_025409_update_table_engine.php ================================================ changeEngine('MyISAM', 'InnoDB'); } public function safeDown() { echo "m150424_025409_update_table_engine cannot be reverted.\n"; $this->changeEngine('InnoDB', 'MyISAM'); } protected function changeEngine($from, $to) { $table = [ '{{%notification}}', '{{%post}}', '{{%post_meta}}', //'{{%post_tag}}', //'{{%setting}}', '{{%user}}', '{{%user_info}}', '{{%user_meta}}', ]; foreach ($table as $key => $value) { $this->execute("ALTER TABLE $value ENGINE = {$to};"); } } } ================================================ FILE: console/migrations/m150424_031429_update_notification_table.php ================================================ addColumn('{{%notification}}', 'status' , Schema::TYPE_BOOLEAN . " UNSIGNED NOT NULL DEFAULT '1' COMMENT '状态 1显示 0不显示' AFTER `data`"); } public function safeDown() { echo "m150424_031429_update_notification_table cannot be reverted.\n"; $this->dropColumn('{{%notification}}', 'status'); return false; } } ================================================ FILE: console/migrations/m150424_100155_update_post_meta_table.php ================================================ addColumn('{{%post_meta}}', 'parent', Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '父级ID' AFTER `name`"); } public function safeDown() { echo "m150424_100155_update_post_meta_table cannot be reverted.\n"; $this->dropColumn('{{%post_meta}}', 'parent'); return false; } } ================================================ FILE: console/migrations/m150425_031844_create_right_link.php ================================================ execute('DROP TABLE IF EXISTS {{%rightlink}}'); $table = '{{%right_link}}'; $this->createTable($table, [ 'id' => Schema::TYPE_PK, 'title' => Schema::TYPE_STRING . ' NOT NULL COMMENT "标题"', 'url' => Schema::TYPE_STRING . '(225) ', 'image' => Schema::TYPE_STRING . '(255) COMMENT "图片链接"', 'content' => Schema::TYPE_STRING . '(255) COMMENT "内容"', 'type' => Schema::TYPE_INTEGER . '(5) COMMENT "展示类别"', 'created_user' => Schema::TYPE_STRING . '(32) NOT NULL COMMENT "创建人"', 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'", ], $this->tableOptions); $this->createIndex('type_index', $table, 'type'); } public function down() { echo "m150425_031844_create_rightLink cannot be reverted.\n"; $this->dropTable('{{%right_link}}'); return false; } } ================================================ FILE: console/migrations/m150626_073539_create_nav.php ================================================ execute('DROP TABLE IF EXISTS {{%nav}}'); $table = '{{%nav}}'; $this->createTable($table, [ 'id' => Schema::TYPE_PK, 'name' => Schema::TYPE_STRING . ' NOT NULL COMMENT "名称"', 'alias' => Schema::TYPE_STRING .' NOT NULL COMMENT "变量(别名)"', 'order' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '99' COMMENT '项目排序'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'", ]); } public function down() { echo "m150626_073539_create_nav cannot be reverted.\n"; $this->dropTable('{{%nav}}'); return false; } /* // Use safeUp/safeDown to run migration code within a transaction public function safeUp() { } public function safeDown() { } */ } ================================================ FILE: console/migrations/m150626_073559_create_nav_url.php ================================================ execute('DROP TABLE IF EXISTS {{%nav_url}}'); $table = '{{%nav_url}}'; $this->createTable($table, [ 'id' => Schema::TYPE_PK, 'nav_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '导航分类ID'", 'title' => Schema::TYPE_STRING . ' NOT NULL COMMENT "标题"', 'url' => Schema::TYPE_STRING . '(225) NOT NULL COMMENT "链接"', 'description' => Schema::TYPE_STRING . '(255) COMMENT "描述"', 'order' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '99' COMMENT '项目排序'", 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '用户ID'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间'", ]); } public function down() { echo "m150626_073559_create_nav_url cannot be reverted.\n"; $this->dropTable('{{%nav_url}}'); return false; } /* // Use safeUp/safeDown to run migration code within a transaction public function safeUp() { } public function safeDown() { } */ } ================================================ FILE: console/migrations/m150702_130239_create_session_init.php ================================================ createTable($this->tableName, [ 'id' => "varchar(40) NOT NULL", 'expire' => "int(11)", 'data' => "blob", ]); $this->addPrimaryKey('idx', $this->tableName, 'id'); $this->createIndex('idx_expire', $this->tableName, 'expire'); $this->addColumn('{{%user_info}}', 'session_id', Schema::TYPE_STRING . "(100) DEFAULT NULL AFTER `last_login_ip`"); } public function down() { echo "m150702_130239_create_session_init cannot be reverted.\n"; $this->dropTable($this->tableName); $this->dropColumn('{{%user_info}}', 'session_id'); return false; } } ================================================ FILE: console/migrations/m150805_085832_create_search_log_table.php ================================================ createTable($this->tableName, [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '用户ID'", 'keyword' => Schema::TYPE_STRING . " NOT NULL DEFAULT '' COMMENT '搜索关键词'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('keyword', $this->tableName, 'keyword'); $this->createIndex('user_id', $this->tableName, 'user_id'); } public function down() { echo "m150805_085832_create_search_log_table cannot be reverted.\n"; $this->dropTable($this->tableName); return false; } } ================================================ FILE: console/migrations/m150808_025734_update_table_character.php ================================================ changeCharacter('utf8mb4', 'utf8mb4_general_ci'); } public function safeDown() { echo "m150720_031448_update_table_character cannot be reverted.\n"; $this->changeCharacter('utf8', 'utf8_general_ci'); } protected function changeCharacter($toA, $toB) { $this->execute("ALTER TABLE {{%post_comment}} MODIFY COLUMN `comment` text CHARACTER SET {$toA} COLLATE {$toB};"); $this->execute("ALTER TABLE {{%post}} MODIFY COLUMN `content` text CHARACTER SET {$toA} COLLATE {$toB};"); $this->execute("ALTER TABLE {{%notification}} MODIFY COLUMN `data` text CHARACTER SET {$toA} COLLATE {$toB};"); } } ================================================ FILE: console/migrations/m150829_091943_update_post_table.php ================================================ addColumn('{{%post}}', 'last_comment_username' , Schema::TYPE_STRING . '(20) DEFAULT NULL COMMENT "最后评论用户" AFTER `tags`'); $this->addColumn('{{%post}}', 'last_comment_time' , Schema::TYPE_INTEGER . ' DEFAULT NULL COMMENT "最后评论用户" AFTER `tags`'); } public function down() { echo "m150829_091943_update_post_table cannot be reverted.\n"; $this->dropColumn('{{%post}}', 'last_comment_time'); $this->dropColumn('{{%post}}', 'last_comment_username'); return false; } } ================================================ FILE: console/migrations/m160320_093621_create_merit_table.php ================================================ db->driverName === 'mysql') { //Mysql 表选项 $engine = $this->useTransaction ? 'InnoDB' : 'MyISAM'; $this->tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=' . $engine; } } public function up() { $this->execute($this->delMeritTable()); $this->createTable('{{%merit_template}}', [ 'id' => Schema::TYPE_PK, 'type' => Schema::TYPE_INTEGER . "(2) DEFAULT 1 COMMENT '类型 1:积分 2:声望 3:徽章'", 'title' => Schema::TYPE_STRING . " NOT NULL COMMENT '标题'", 'unique_id' => Schema::TYPE_STRING . " NOT NULL COMMENT 'action uniqueId'", 'method' => Schema::TYPE_INTEGER . "(2) DEFAULT 2 COMMENT '请求方式 1 get 2 post'", 'event' => Schema::TYPE_INTEGER . "(2) DEFAULT 0 COMMENT '事件 0:不绑定'", 'action_type' => Schema::TYPE_INTEGER . "(2) DEFAULT 2 COMMENT '操作类型 1减去 2新增'", 'rule_key' => Schema::TYPE_INTEGER . "(2) DEFAULT 0 COMMENT '规则类型 0:不限制 1:按天限制 2:按次限制'", 'rule_value' => Schema::TYPE_INTEGER . " DEFAULT 0 COMMENT '规则值'", 'increment' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '变化值'", 'status' => Schema::TYPE_BOOLEAN . " DEFAULT 1 COMMENT '状态 0暂停 1开启'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '更新时间'", ], $this->tableOptions); $this->createIndex('type', '{{%merit_template}}', 'type'); $this->createIndex('unique_id', '{{%merit_template}}', 'unique_id'); $this->createTable('{{%merit}}', [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '用户ID'", 'username' => Schema::TYPE_STRING . "(20) DEFAULT NULL COMMENT '用户名'", 'type' => Schema::TYPE_INTEGER . "(2) DEFAULT 1 COMMENT '类型 1:积分 2:声望 3:徽章'", 'merit' => Schema::TYPE_INTEGER . " DEFAULT NULL COMMENT '总值'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", 'updated_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '更新时间'", ], $this->tableOptions); $this->createIndex('type', '{{%merit}}', 'type'); $this->createIndex('user_id', '{{%merit}}', 'user_id'); $this->createTable('{{%merit_log}}', [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_INTEGER . " UNSIGNED NULL NULL COMMENT '用户ID'", 'username' => Schema::TYPE_STRING . "(20) DEFAULT NULL COMMENT '用户名'", 'merit_template_id' => Schema::TYPE_INTEGER . " UNSIGNED NULL NULL COMMENT '模板ID'", 'type' => Schema::TYPE_INTEGER . "(2) DEFAULT 1 COMMENT '类型 1:积分 2:声望 3:徽章'", 'description' => Schema::TYPE_STRING . " NOT NULL COMMENT '描述'", 'action_type' => Schema::TYPE_INTEGER . "(2) DEFAULT 2 COMMENT '操作类型 1减去 2新增'", 'increment' => Schema::TYPE_INTEGER . " UNSIGNED DEFAULT NULL COMMENT '变化值'", 'created_at' => Schema::TYPE_INTEGER . " UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间'", ], $this->tableOptions); $this->createIndex('type', '{{%merit_log}}', 'type'); $this->createIndex('user_id', '{{%merit_log}}', 'user_id'); $this->createIndex('merit_template_id', '{{%merit_log}}', 'merit_template_id'); } public function down() { echo "m150807_082458_create_merit_table cannot be reverted.\n"; $this->dropTable('{{%merit_template}}'); $this->dropTable('{{%merit}}'); $this->dropTable('{{%merit_log}}'); return false; } /** * @return string */ private function delMeritTable() { return 'DROP TABLE IF EXISTS {{%merit_template}}; DROP TABLE IF EXISTS {{%merit}}; DROP TABLE IF EXISTS {{%merit_log}}; '; } } ================================================ FILE: console/migrations/m160321_132724_add_donate_table.php ================================================ createTable($this->tableName, [ 'id' => $this->primaryKey(), 'user_id' => $this->integer()->notNull(), 'status' => $this->boolean()->defaultValue(1), 'description' => $this->string(), 'qr_code' => $this->string(), 'created_at' => $this->integer(), 'updated_at' => $this->integer(), ], $this->tableOptions); $this->createIndex('user_id', $this->tableName, 'user_id'); } public function down() { echo "m160321_132724_add_donate_table cannot be reverted.\n"; $this->dropTable($this->tableName); return false; } } ================================================ FILE: console/migrations/m160719_093527_modify_user.php ================================================ createIndex('idx_username', $this->userTable, 'username', true); } public function down() { $this->dropIndex('idx_username', $this->userTable); } /* // Use safeUp/safeDown to run migration code within a transaction public function safeUp() { } public function safeDown() { } */ } ================================================ FILE: console/migrations/m190624_022722_create_spam_table.php ================================================ db->driverName === 'mysql') { //Mysql 表选项 $engine = $this->useTransaction ? 'InnoDB' : 'MyISAM'; $this->tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=' . $engine; } } /** * {@inheritdoc} */ public function safeUp() { $this->createTable('{{%spam}}', [ 'id' => $this->primaryKey(), 'status' => $this->tinyInteger(1)->defaultValue(1)->comment('0 or 1'), 'content' => $this->text()->notNull(), 'type' => $this->string(20)->defaultValue('contains')->comment('contains or similar'), 'for' => $this->string(20)->defaultValue('all'), ], $this->tableOptions); $this->createIndex('fk_status_for', '{{%spam}}', ['status', 'for']); self::initData(); } /** * {@inheritdoc} */ public function safeDown() { $this->dropTable('{{%spam}}'); } public static function initData() { Spam::create(Spam::TYPE_CONTAINS, '网{3}赌'); Spam::create(Spam::TYPE_CONTAINS, '找小姐'); Spam::create(Spam::TYPE_SIMILAR, '网赌平台冻账号说我违规套利不给出款该怎么办?'); } } ================================================ FILE: console/migrations/m190908_053628_init_admin.php ================================================ createFounder(); } public function down() { echo "m190908_053628_init_admin cannot be reverted.\n"; return false; } /** * 创建创始人数据 */ public function createFounder() { Console::output("\n请先创建创始人账户: "); $user = $this->saveFounderData(new \frontend\models\SignupForm()); $user ? $user->id : 1; // 用户创建成功则指定用户id,否则指定id为1的用户为创始人. Console::output("创始人创建" . ($user ? '成功' : "失败,请手动创建创始人用户\n")); } /** * 用户创建交互 * @param $_model * @return mixed */ private function saveFounderData($_model) { /** @var \frontend\models\SignupForm $model */ $model = clone $_model; $model->username = Console::prompt('请输入创始人用户名', ['default' => 'admin']); $model->email = Console::prompt('请输入创始人邮箱', ['default' => 'admin@admin.com']); $model->password = Console::prompt('请输入创始人密码', ['default' => '123456']); $model->role = \common\models\User::ROLE_SUPER_ADMIN; if (!($user = $model->signup())) { Console::output(Console::ansiFormat("\n输入数据验证错误:", [Console::FG_RED])); foreach ($model->getErrors() as $k => $v) { Console::output(Console::ansiFormat(implode("\n", $v), [Console::FG_RED])); } if (Console::confirm("\n是否重新创建创始人账户:")) { $user = $this->saveFounderData($_model); } } return $user; } } ================================================ FILE: console/migrations/m190908_055507_init_data.php ================================================ generateFakeData(rand(20, 100)); } } public function down() { echo "m190908_055507_init_data cannot be reverted.\n"; return false; } public function generateFakeData($num) { Console::startProgress(0, 100); $topic = new Topic(); $comment = new PostComment(); $node = new PostMeta(); $faker = Faker\Factory::create('zh_CN'); $nodeData = [ ['name' => '分享', 'alias' => '', 'parent' => 0], ['name' => '招聘', 'alias' => 'jobs', 'parent' => 1], ['name' => '瞎扯淡', 'alias' => 'booshit', 'parent' => 1], ['name' => '健康', 'alias' => 'health', 'parent' => 1], ['name' => '创业', 'alias' => 'startup', 'parent' => 1], ]; $transaction = Yii::$app->db->beginTransaction(); try { for ($j = 0; $j < count($nodeData); $j++) { $_node = clone $node; $_node->setAttributes($nodeData[$j] + ['type' => 'topic_category']); $_node->save(); } $this->execute("INSERT INTO {{%merit_template}} (`id`, `type`, `title`, `unique_id`, `method`, `event`, `action_type`, `rule_key`, `rule_value`, `increment`, `status`, `created_at`, `updated_at`) VALUES (1, 1, '登录', 'site/login', 2, 0, 2, 1, 1, 2, 1, 1458657160, 1458823425), (2, 1, '发帖', 'topic/default/create', 2, 0, 2, 0, NULL, 6, 1, 1458657218, 1458657218), (3, 1, '回复', 'topic/comment/create', 2, 0, 2, 0, NULL, 4, 1, 1458657251, 1458657251), (4, 1, '发动弹', 'tweet/default/create', 2, 0, 2, 0, NULL, 4, 1, 1458657296, 1468647701); "); /** @var User $user */ $user = User::find()->where(['role' => User::ROLE_SUPER_ADMIN])->one(); Yii::$app->user->setIdentity($user); for ($i = 1; $i <= $num; $i++) { $_topic = clone $topic; $_topic->setAttributes([ 'type' => Topic::TYPE, 'title' => $faker->text(rand(10, 50)), 'post_meta_id' => rand(2, 4), 'status' => rand(1, 2), 'content' => $faker->text(rand(100, 2000)), 'user_id' => 1 ]); if (!$_topic->save()) { throw new Exception(array_values($_topic->getFirstErrors())[0]); } for ($_i = 1; $_i <= rand(1, 20); $_i++) { $_comment = clone $comment; $_comment->setAttributes([ 'comment' => $faker->text(rand(100, 2000)), 'post_id' => $_topic->id, 'ip' => '127.0.0.1', 'user_id' => 1 ]); if (!$_comment->save()) { throw new Exception(array_values($_comment->getFirstErrors())[0]); } // 更新回复时间 $_topic->lastCommentToUpdate($user['username']); // 评论计数器 Topic::updateAllCounters(['comment_count' => 1], ['id' => $_topic->id]); // 更新个人总统计 UserInfo::updateAllCounters(['comment_count' => 1], ['user_id' => $_topic->user_id]); } Console::updateProgress($i / $num * 100, 100); } $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); throw $e; } Console::endProgress(); } } ================================================ FILE: console/models/.gitkeep ================================================ * ================================================ FILE: console/runtime/.gitignore ================================================ * !.gitignore ================================================ FILE: doc/README.md ================================================ ## 本文件夹是功能详细设计文档 本文档主要由 @forecho 设计。 ================================================ FILE: docker-files/docker-compose-example.yml ================================================ mysql: image: mysql environment: - MYSQL_DATABASE=getyii - MYSQL_ROOT_PASSWORD=getyii.com ports: - 3306:3306 web: build: . links: - mysql:mysql ports: - 80:80 environment: - MYSQL_INSTANCE_NAME=getyii - MYSQL_PASSWORD=getyii.com - APP_ENV=Production volumes: - ./vendor:/app/vendor:rw - ~/.composer/cache:/root/.composer/cache:rw # - .:/app:rw # you should uncomment this line if you want to develop the app. ================================================ FILE: docker-files/getyii.com.conf ================================================ server { charset utf-8; client_max_body_size 128M; listen 80; server_name getyii.com www.getyii.com *.dev.www.getyii.com; index index.php; set $rootdir /app/frontend/web; root $rootdir; location / { try_files $uri $uri /index.php?$args; } location ~ \.php$ { include fastcgi_params; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $rootdir/index.php; fastcgi_pass 127.0.0.1:9000; } location ~ /\.(ht|svn|git) { deny all; } } server { charset utf-8; client_max_body_size 128M; listen 80; server_name admin.getyii.com *.dev.admin.getyii.com; index index.php; set $rootdir /app/backend/web; root $rootdir; location / { try_files $uri $uri /index.php?$args; } location ~ \.php$ { include fastcgi_params; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $rootdir/index.php; fastcgi_pass 127.0.0.1:9000; } location ~ /\.(ht|svn|git) { deny all; } } ================================================ FILE: docker-files/run.sh ================================================ #!/bin/bash set -e -x cd /app composer install --prefer-dist --no-interaction --optimize-autoloader ./init --env=${APP_ENV:-Production} --overwrite=y ./yii migrate --interactive=0 function setEnvironmentVariable() { if [ -z "$2" ]; then echo "Environment variable '$1' not set." return fi echo "env[$1] = \"$2\" ; automatically add env" >> /usr/local/etc/php-fpm.conf } sed -i '/automatically add env/d' /usr/local/etc/php-fpm.conf # Grep all ENV variables for _curVar in `env | awk -F = '{print $1}'`;do # awk has split them by the equals sign # Pass the name and value to our function setEnvironmentVariable ${_curVar} ${!_curVar} done supervisord -n # service supervisord start ================================================ FILE: environments/dev/backend/config/main-local.php ================================================ [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => '', ], ], ]; if (!YII_ENV_TEST) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = 'yii\debug\Module'; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = 'yii\gii\Module'; } return $config; ================================================ FILE: environments/dev/backend/config/params-local.php ================================================ run(); ================================================ FILE: environments/dev/backend/web/index.php ================================================ run(); ================================================ FILE: environments/dev/common/config/db.php ================================================ 'yii\db\Connection', 'dsn' => 'mysql:host=' . $MYSQL_PORT_3306_TCP_ADDR . ';dbname=' . $MYSQL_DB_NAME, 'username' => $MYSQL_USERNAME, 'password' => $MYSQL_PASSWORD, 'charset' => 'utf8mb4', 'enableSchemaCache' => true, 'schemaCacheDuration' => 3600, 'schemaCache' => 'cache', ]; return $db; ================================================ FILE: environments/dev/common/config/main-local.php ================================================ [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => '', ], ], ]; if (!YII_ENV_TEST) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = 'yii\debug\Module'; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = 'yii\gii\Module'; } return $config; ================================================ FILE: environments/dev/frontend/config/params-local.php ================================================ run(); ================================================ FILE: environments/dev/frontend/web/index.php ================================================ run(); ================================================ FILE: environments/dev/yii ================================================ #!/usr/bin/env php run(); exit($exitCode); ================================================ FILE: environments/index.php ================================================ [ * 'path' => 'directory storing the local files', * 'setWritable' => [ * // list of directories that should be set writable * ], * 'setExecutable' => [ * // list of directories that should be set executable * ], * 'setCookieValidationKey' => [ * // list of config files that need to be inserted with automatically generated cookie validation keys * ], * 'createSymlink' => [ * // list of symlinks to be created. Keys are symlinks, and values are the targets. * ], * ], * ]; * ``` */ return [ 'Development' => [ 'path' => 'dev', 'setWritable' => [ 'backend/runtime', 'backend/web/assets', 'frontend/runtime', 'frontend/web/assets', ], 'setExecutable' => [ 'yii', ], 'setCookieValidationKey' => [ 'backend/config/main-local.php', 'frontend/config/main-local.php', ], ], 'Production' => [ 'path' => 'prod', 'setWritable' => [ 'backend/runtime', 'backend/web/assets', 'frontend/runtime', 'frontend/web/assets', ], 'setExecutable' => [ 'yii', ], 'setCookieValidationKey' => [ 'backend/config/main-local.php', 'frontend/config/main-local.php', ], ], ]; ================================================ FILE: environments/prod/backend/config/main-local.php ================================================ [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => '', ], ], ]; ================================================ FILE: environments/prod/backend/config/params-local.php ================================================ run(); ================================================ FILE: environments/prod/common/config/db.php ================================================ 'yii\db\Connection', 'dsn' => 'mysql:host=' . $MYSQL_PORT_3306_TCP_ADDR . ';dbname=' . $MYSQL_DB_NAME, 'username' => $MYSQL_USERNAME, 'password' => $MYSQL_PASSWORD, 'charset' => 'utf8mb4', 'enableSchemaCache' => true, 'schemaCacheDuration' => 3600, 'schemaCache' => 'cache', ]; return $db; ================================================ FILE: environments/prod/common/config/main-local.php ================================================ [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => '', ], ], ]; ================================================ FILE: environments/prod/frontend/config/params-local.php ================================================ run(); ================================================ FILE: environments/prod/yii ================================================ #!/usr/bin/env php run(); exit($exitCode); ================================================ FILE: frontend/assets/AppAsset.php ================================================ * @since 2.0 */ class AppAsset extends AssetBundle { public $basePath = '@webroot'; public $baseUrl = '@web'; public $css = [ 'css/global.css', 'css/site.css', //site.css or site-ruyi.css ]; public $js = [ 'js/emojify.min.js', 'js/main.js', 'js/topic.js', 'js/jquery.pin.js', 'js/nav.js', ]; public $depends = [ 'yii\web\YiiAsset', 'yii\bootstrap\BootstrapAsset', ]; } ================================================ FILE: frontend/assets/AtJsAsset.php ================================================ * createTime : 2016/3/10 11:24 * description: */ namespace frontend\assets; use yii\web\AssetBundle; class AtJsAsset extends AssetBundle { public $basePath = '@webroot'; public $baseUrl = '@web'; public $css = [ ]; public $js = [ 'js/At.js', ]; public $depends = [ 'yii\web\YiiAsset', 'common\assets\AtJs', 'common\assets\CaretJs', ]; } ================================================ FILE: frontend/assets/BowerAsset.php ================================================ */ class BowerAsset extends AssetBundle { public $sourcePath = '@bower'; public $baseUrl = '@bower'; public $css = [ 'highlightjs/styles/darkula.css', 'pace/themes/green/pace-theme-minimal.css', ]; public $js = [ 'highlightjs/highlight.pack.js', 'localforage/dist/localforage.min.js', 'pace/pace.min.js', ]; } ================================================ FILE: frontend/assets/EditorAsset.php ================================================ */ class EmojifyAsset extends AssetBundle { public $sourcePath = '@bower/emojify.js'; public $baseUrl = '@bower/emojify.js'; public $css = [ ]; public $js = [ 'dist/js/emojify.min.js', ]; } ================================================ FILE: frontend/behaviors/AfterLoginBehavior.php ================================================ 'afterLogin', ]; } /** * @param \yii\web\UserEvent $event * @return bool */ public function afterLogin($event) { if ($model = $event->identity->userInfo) { $model->login_count += 1; $model->prev_login_time = $model->last_login_time; $model->prev_login_ip = $model->last_login_ip; $model->last_login_time = time(); $model->last_login_ip = Yii::$app->getRequest()->getUserIP(); if (!Yii::$app->session->isActive) { Yii::$app->session->open(); } $model->session_id = Yii::$app->session->id; Yii::$app->session->close(); if ($model->save()) { return true; } } return false; } } ================================================ FILE: frontend/config/.gitignore ================================================ main-local.php params-local.php ================================================ FILE: frontend/config/bootstrap.php ================================================ 'app-frontend', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'controllerNamespace' => 'frontend\controllers', 'components' => [ 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ '/' => 'topic/default/index', '' => 'site/', '' => 'topic/default/', 'member/' => 'user/default/show', 'member//' => 'user/default/', '//' => '/', 'member///' => 'user/action/', 'tag/' => 'topic/default/index/', 'node/' => 'topic/default/index', 'topic/' => 'topic/default/view', '///' => '//', ], ], 'user' => [ 'identityClass' => 'common\models\User', 'enableAutoLogin' => true, 'as afterLogin' => 'frontend\behaviors\AfterLoginBehavior', ], 'xunsearch' => [ 'class' => 'hightman\xunsearch\Connection', // 此行必须 'iniDirectory' => '@frontend/config', // 搜索 ini 文件目录,默认:@vendor/hightman/xunsearch/app 'charset' => 'utf-8', // 指定项目使用的默认编码,默认即时 utf-8,可不指定 ], 'authClientCollection' => [ 'class' => 'yii\authclient\Collection', 'clients' => [ // 'google' => [ // 'class' => 'yii\authclient\clients\GoogleOpenId' // ], 'github' => [ 'class' => 'yii\authclient\clients\GitHub', 'clientId' => 'github_client_id', 'clientSecret' => 'github_client_secret', 'viewOptions' => [ 'popupWidth' => 820, 'popupHeight' => 600, ] ], ], ], 'i18n' => [ 'translations' => [ 'exception*' => [ 'class' => 'yii\i18n\PhpMessageSource', 'basePath' => '@frontend/messages', 'fileMap' => [ 'app' => 'app.php', ], ], ], ], 'errorHandler' => [ 'errorAction' => 'site/error', ], ], 'modules' => [ 'user' => [ 'class' => 'frontend\modules\user\Module', ], 'topic' => [ 'class' => 'frontend\modules\topic\Module', ], 'nav' => [ 'class' => 'frontend\modules\nav\Module', ], 'tweet' => [ 'class' => 'frontend\modules\tweet\Module', ], ], 'params' => $params, ]; ================================================ FILE: frontend/config/params.php ================================================ 'admin@example.com', 'donateNode' => ['tricks'], // 开启打赏分类 'loginNode' => ['jobs'], // 需要登录才能访问的分类 'donateTag' => ['求打赏', '技巧库'], // 开启标签 'postingIntervalLimit' => 3600, // 限制发帖间隔,单位秒。默认是 1 个小时 'setting' => [ 'xunsearch' => false, // true 表示开启 GetYii xunsearch 搜索功能,默认不开启 ], ]; ================================================ FILE: frontend/config/search.ini ================================================ project.name = getyii project.default_charset = utf-8 server.index = 8383 server.search = 8384 [topic_id] type = id [title] type = title [content] type = body [status] index = self tokenizer = full [updated_at] type = numeric ================================================ FILE: frontend/controllers/NotificationController.php ================================================ [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], 'clear' => ['post'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ ['allow' => true, 'actions' => ['index', 'count'], 'roles' => ['@']], ['allow' => true, 'actions' => ['delete', 'clear'], 'verbs' => ['POST'], 'roles' => ['@']], ] ] ]); } /** * Lists all Notification models. * @return mixed */ public function actionIndex() { $dataProvider = new ActiveDataProvider([ 'query' => Notification::find()->where(['user_id' => Yii::$app->user->id]), 'sort' => ['defaultOrder' => [ 'created_at' => SORT_DESC, 'id' => SORT_ASC, ]] ]); $notifyCount = UserService::findNotifyCount(); UserService::clearNotifyCount(); return $this->render('index', [ 'dataProvider' => $dataProvider, 'notifyCount' => $notifyCount, ]); } /** * 返回通知条数 * @return mixed */ public function actionCount() { $model = User::findOne(Yii::$app->user->id); return $model->notification_count; } /** * Deletes an existing Notification model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * 清空通知 * @return \yii\web\Response * @throws NotFoundHttpException * @throws \Exception */ public function actionClear() { Notification::deleteAll(['user_id' => Yii::$app->user->id]); return $this->redirect(['index']); } /** * Finds the Notification model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return Notification the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = Notification::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } } ================================================ FILE: frontend/controllers/PostTagController.php ================================================ request->queryParams; $dataProvider = $searchModel->search($params); //ajax if (Yii::$app->request->getIsAjax()) { return json_encode(ArrayHelper::getColumn($dataProvider->getModels(), function ($model) { return $model->getAttributes(['name']); })); } return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } } ================================================ FILE: frontend/controllers/SiteController.php ================================================ [ 'class' => AccessControl::className(), 'only' => ['logout', 'signup', 'connect', 'upload'], 'rules' => [ ['actions' => ['signup', 'connect'], 'allow' => true, 'roles' => ['?']], ['actions' => ['logout', 'upload'], 'allow' => true, 'roles' => ['@']], ], ], 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => ['logout' => ['post']], ], ]); } /** * @inheritdoc */ public function actions() { return [ 'error' => [ 'class' => 'yii\web\ErrorAction', ], 'qr' => [ 'class' => QrCodeAction::className(), 'component' => 'qr' // if configured in our app as `qr` ], 'captcha' => [ 'class' => 'yii\captcha\CaptchaAction', 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, ], ]; } /** * @inheritdoc */ public function beforeAction($action) { if ($action->id == 'upload') { $this->enableCsrfValidation = false; } return parent::beforeAction($action); } public function actionIndex() { $topics = Post::find()->with('user', 'category')->limit(20)->where(['status' => 2])->orderBy(['created_at' => SORT_DESC])->all(); $users = UserService::findActiveUser(12); $headline = Arr::getColumn(RightLink::find()->where(['type' => RightLink::RIGHT_LINK_TYPE_HEADLINE])->all(), 'content'); $statistics = []; $statistics['post_count'] = Post::find()->count(); $statistics['comment_count'] = PostComment::find()->count(); $statistics['online_count'] = Session::find()->where(['>', 'expire', time()])->count(); return $this->render('index', [ 'topics' => $topics, 'users' => $users, 'statistics' => $statistics, 'headline' => Arr::arrayRandomAssoc($headline), ]); } public function actionLogin() { if (!\Yii::$app->user->isGuest) { return $this->goHome(); } $model = new LoginForm(); if ($model->load(Yii::$app->request->post()) && $model->login()) { return $this->goBack(); } else { return $this->render('login', [ 'model' => $model, ]); } } public function actionLogout() { Yii::$app->user->logout(); return $this->goHome(); } public function actionContact() { $model = new ContactForm(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { if ($model->sendEmail(Yii::$app->params['adminEmail'])) { Yii::$app->session->setFlash('success', 'Thank you for contacting us. We will respond to you as soon as possible.'); } else { Yii::$app->session->setFlash('error', 'There was an error sending email.'); } return $this->refresh(); } else { return $this->render('contact', [ 'model' => $model, ]); } } public function actionAbout() { return $this->render('about'); } public function actionTags() { $tags = PostTag::find()->orderBy('updated_at DESC')->all(); return $this->render('tags', [ 'tags' => $tags, ]); } public function actionContributors() { return $this->render('contributors'); } public function actionGetstart() { return $this->render('getstart'); } public function actionUsers() { $model = UserService::findActiveUser(100); $count = User::find()->where(['status' => 10])->count(); return $this->render('users', [ 'model' => $model, 'count' => $count, ]); } public function actionAtUsers() { $model = UserService::findActiveUser(400); return Json::encode(Arr::getColumn($model, 'username')); } public function actionBook() { return $this->redirect('http://book.getyii.com'); } public function actionMarkdown() { return $this->render('markdown'); } public function actionTimeline() { return $this->render('timeline'); } /** * Displays page where user can create new account that will be connected to social account. * @param integer $account_id * @return string * @throws NotFoundHttpException */ public function actionConnect($account_id) { /** @var UserAccount $account */ $account = UserAccount::find()->where(['id' => $account_id])->one(); if ($account === null || $account->getIsConnected()) { throw new NotFoundHttpException; } $accountData = Json::decode($account->data); $model = new SignupForm(); $model->username = $accountData['login']; $model->email = empty($accountData['email']) ? '' : $accountData['email']; $this->performAjaxValidation($model); if ($model->load(Yii::$app->request->post())) { if ($user = $model->signup()) { $account->user_id = $user->id; $account->save(false); if (Yii::$app->getUser()->login($user, 1209600)) { return $this->goHome(); } } } return $this->render('signup', [ 'model' => $model, ]); } public function actionSignup() { $model = new SignupForm(); $this->performAjaxValidation($model); if ($model->load(Yii::$app->request->post())) { $model->role = User::ROLE_USER; if ($user = $model->signup()) { if (Yii::$app->getUser()->login($user)) { return $this->goHome(); } } } return $this->render('signup', [ 'model' => $model, ]); } public function actionRequestPasswordReset() { $model = new PasswordResetRequestForm(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { if ($model->sendEmail()) { Yii::$app->getSession()->setFlash('success', 'Check your email for further instructions.'); return $this->goHome(); } else { Yii::$app->getSession()->setFlash('error', 'Sorry, we are unable to reset password for email provided.'); } } return $this->render('requestPasswordResetToken', [ 'model' => $model, ]); } public function actionResetPassword($token) { try { $model = new ResetPasswordForm($token); } catch (InvalidParamException $e) { throw new BadRequestHttpException($e->getMessage()); } if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) { Yii::$app->getSession()->setFlash('success', 'New password was saved.'); return $this->goHome(); } return $this->render('resetPassword', [ 'model' => $model, ]); } /** * 上传图片 * @param $field * @return array */ public function actionUpload($field) { Yii::$app->response->format = Response::FORMAT_JSON; $token = params('smToken'); $file = UploadHelper::getCurlValue($_FILES[$field]['tmp_name'], $_FILES[$field]['type'], basename($_FILES[$field]['name'])); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://sm.ms/api/v2/upload'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0'); curl_setopt($ch, CURLOPT_POSTFIELDS, ['smfile' => $file]); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization:" . $token ]); $response = curl_exec($ch); if (curl_errno($ch)) { Yii::error($response, '上传图片失败'); $return = ['success' => 0, 'message' => '上传失败']; } else { $array = Json::decode($response); if (!empty($array['data']['url'])) { $return = ['success' => 1, 'message' => '上传成功', 'url' => $array['data']['url']]; } else { Yii::error($response, '上传图片失败'); $return = ['success' => 0, 'message' => '上传失败']; } } curl_close($ch); return $return; } /** * Performs ajax validation. * @param Model $model * @return void * @throws \yii\base\ExitException */ protected function performAjaxValidation($model) { if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) { Yii::$app->response->format = Response::FORMAT_JSON; Yii::$app->response->data = ActiveForm::validate($model); Yii::$app->response->send(); Yii::$app->end(); } } } ================================================ FILE: frontend/messages/zh-CN/app.php ================================================ '社区', 'Wiki' => 'Wiki', 'Users' => '会员', 'About' => '关于', 'Search' => '搜索', 'Login' => '登录', 'Logout' => '退出', 'Sign up' => '注册', 'Notifications' => '通知提醒', 'New Topic' => '发布新帖', 'Links' => '友情链接', 'Same Node Topics' => '节点下其他主题', 'Tips and Tricks' => '小贴士', 'Site Status' => '统计信息', 'Total User' => '社区会员', 'Total Topic' => '主题数', 'Total Reply' => '回复数', 'You dont have permission to proceed.' => '很抱歉, 当前用户没有权限继续操作.
有什么问题请联系管理员.', 'Permission Deny' => '管理员权限提示', 'Notice' => '提示', 'Login with Github' => '使用 Github 帐号登录', 'User Login Require' => '用户登录提示', 'You need to login to proceed.' => '需要登录后才能继续操作.
当前只允许通过 Github 帐号登录.', 'Create New Account' => '创建新账号', 'Avatar' => '头像', 'Username' => '用户名', 'Email' => '邮箱', 'Confirm' => '确定', 'Operation Deny' => '操作被禁止', 'Sorry, You account is banned.' => '对不起,您的账号已被禁用', 'All Nodes' => '节点导航', 'My Notifications' => '我的提醒', 'Your topic have new reply:' => '回复了你的主题:', 'Your follow topic have new reply:' => '回复了你关注的主题:', 'Attented topic has new reply:' => '回复了你关注的主题:', 'Mention you At:' => '在主题中提及你:', 'Mention you topic At:' => '在主题中提及你:', 'Mention you tweet At:' => '在动弹中提及你:', 'Data has been deleted.' => '信息已被删除.', 'You dont have any notice yet!' => '还未收到提醒!', 'About Us' => '关于我们', 'Excellent Topics' => '社区精华帖', 'More Excellent Topics' => '查看更多精华帖', 'Community Wiki' => '社区 Wiki', 'Dont have any data Yet' => '还未有主题', 'Recent' => '最近发表', 'Excellent' => '精华主题', 'Vote' => '最多投票', 'Noreply' => '无人问津', 'Publish' => '发布', 'at' => '于', 'Last Reply by' => '最后由', 'Reads' => '阅读', 'Are you sure want to delete is topic?' => '确定要删除此主题吗?', 'This topic has been mark as Excenllent Topic.' => '本帖已被设为精华帖!', 'This is a Community Wiki.' => '本帖已被设为社区 Wiki!', 'Share on Weibo' => '分享到微博', 'Share on Twitter' => '分享到 Twitter', 'Share on Facebook' => '分享到 Facebook', 'Share on Google Plus' => '分享到 Google Plus', 'Cancel' => '取消', 'Attent' => '关注', 'Favorite' => '收藏', 'Mark as Excellent' => '设为推荐主题', 'Mark as Community Wiki' => '加入到社区 Wiki', 'Pin it on To' => '置顶此主题', 'Delete' => '删除', 'Edit' => '编辑', 'Preview' => '预览', 'No content.' => '没有内容', 'Please using markdown.' => '请使用 Markdown 格式书写 ;-)', 'Current Node' => '当前节点', 'Pick a node' => '请选择节点', 'Please write down a topic' => '请填写标题', 'Writting Format Notice' => '格式说明', 'This kind of topic is not allowed.' => '以下类型的信息会污染我们的社区', 'We can benefit from it.' => '在高质量优秀社区的我们', 'Received {0} reply' => '共收到 {0} 条回复', 'User ID:' => '用户 ID:', 'Real Name' => '姓名', 'Company' => '公司', 'City' => '城市', 'Blog' => '博客', 'Signature' => '签名', 'Edit Profile' => '编辑个人资料', 'Are you sure want to block this User?' => '您确定要封停该用户吗?', 'Are you sure want to unblock this User?' => '您确定要解封该用户吗?', 'Unblock User' => '解封用户', 'Block User' => '封停用户', 'This user is banned!' => '该用户已被封停', 'Basic Info' => '个人信息', 'Favorites' => '收藏', 'Replies' => '回复', 'avatar_notice' => '如需修改头像,请到 Github 的个人设置 页面修改, 然后点击链接 ', 'Update Cache' => '更新缓存', 'twitter_placeholder' => '你的 Twitter 帐号, 不需要加前缀 https://twitter.com/', 'personal_website_placebolder' => '你的个人网站, 不需要加前缀 http://', 'signature_placeholder' => '签名/座右铭', 'introduction_placeholder' => '个人简介', 'Dont have any favorites yet' => '还未收藏任何主题', 'Newly Registered User List' => '新加入的会员列表', 'Reply List' => '主题列表', 'Topic List' => '主题列表', 'Dont have any comment yet' => '还未留下任何回复', 'Recent Replies' => '最新回复', 'Recent Topics' => '最近主题', 'Reply' => '回复', 'User Login Required for commenting.' => '需要登录后才能发表回复.', 'site_intro' => 'Get Yii,对!没错!这里就是 Yii 社区,我们想做国内最权威的 Yii 社区,拥有国内所有资深的 Yii 工程师。', 'contributing' => '功能正在完善中, 欢迎 贡献代码提交 Issue.', 'be_nice' => '我们希望 PHPHub 能够成为技术氛围最好的 PHP 社区,而实现这个目标,需要我们所有人的共同努力:友善,公平,尊重知识和事实。', 'publish_typography' => '请注意单词拼写,以及中英文排版,参考此页', 'publish_markdown' => '支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法', 'publish_emoji' => '支持表情,见 Emoji cheat sheet', 'publish_at_user' => '@name 会链接到用户页面,并会通知他', 'publish_image' => '暂不支持上传图片,请使用外链图片。推荐图床:drp.ioimgur', 'Successfully remove attention.' => '成功取消关注', 'Successfully_attention' => '成功关注主题, 系统会通知你关于此主题最新的讨论.', 'Operation succeeded.' => '操作成功!', 'Congratulations and Welcome!' => '恭喜, 你已经成功加入 PHPHub.', 'Operation failed!' => '操作失败!', 'Favorited your topic:' => '收藏了你的主题', 'Thanks your topic:' => '感谢了你的主题', 'Attented your topic:' => '关注了你的主题', 'Up Vote your topic' => '赞了你的主题', 'Up Vote your reply' => '赞了你的回复', 'Up Vote your tweet' => '赞了你的动弹', 'has mark your topic as wiki:' => '把你的主题收录为社区 Wiki', 'has recomended your topic:' => '推荐了你的主题', 'Are you sure want to logout?' => '你确定要退出吗?', 'No comments' => '暂无回复', 'Learning Resources' => '推荐学习资源', 'Create New Topic' => '创建新主题', 'Can not vote your feedback' => '不允许给自己点赞', 'Recomended Resources' => '推荐资源', 'Stick' => '置顶', 'Recommended' => '推荐', 'Refresh cache success' => '刷新缓存成功', 'Append' => '附言', 'Append Content' => '添加附言', 'append_notice' => '附加内容, 使用此功能的话, 会给所有参加过讨论的人发送提醒.', 'Submit' => '提交', 'Close' => '关闭', 'Attented topic has new update:' => '关注的话题有新附言', 'Commented topic has new update:' => '留言的话题有新附言', 'File has to be smaller than 2MB' => '文件最大支持2M', 'Document' => '文档', 'Update Post: ' => '更新话题:', 'Data Deleted' => '相关数据已经删除', 'Online Count' => '当前在线人数', 'Post Count' => '主题数', 'Comment Count' => '回答数', 'Donate' => '打赏', 'Qr Code' => '二维码', 'Status' => '状态', 'Save' => '保存', 'Description' => '描述', 'Tweet Content' => '今天你有什么想分享的或者想吐槽的?(你可以 @forecho AT用户,支持 :joy: emoji 表情,支持 #干货分享# 话题功能)', 'cc {username} {url}' => ' > 本文由 [{username}]({url}) 创作,采用 [知识共享署名 3.0 中国大陆许可协议](http://creativecommons.org/licenses/by/3.0/cn) 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。', ]; ================================================ FILE: frontend/models/ContactForm.php ================================================ 'Verification Code', ]; } /** * Sends an email to the specified email address using the information collected by this model. * * @param string $email the target email address * @return boolean whether the email was sent */ public function sendEmail($email) { return Yii::$app->mailer->compose() ->setTo($email) ->setFrom([$this->email => $this->name]) ->setSubject($this->subject) ->setTextBody($this->body) ->send(); } } ================================================ FILE: frontend/models/Notification.php ================================================ 255] ]; } public function getUser() { return $this->hasOne(User::className(), ['id' => 'user_id']); } public function getFromUser() { return $this->hasOne(User::className(), ['id' => 'from_user_id']); } public function getPost() { return $this->hasOne(Post::className(), ['id' => 'post_id']); } public function getLable($type) { switch ($type) { case 'new_comment': $lable = Yii::t('app', 'Your follow topic have new reply:'); break; case 'attention': $lable = Yii::t('app', 'Attented topic has new reply:'); break; case 'at': $lable = Yii::t('app', 'Mention you At:'); break; case 'at_topic': $lable = Yii::t('app', 'Mention you topic At:'); break; case 'at_tweet': $lable = Yii::t('app', 'Mention you tweet At:'); break; case 'topic_favorite': $lable = Yii::t('app', 'Favorited your topic:'); break; case 'topic_thanks': $lable = Yii::t('app', 'Thanks your topic:'); break; case 'topic_follow': $lable = Yii::t('app', 'Attented your topic:'); break; case 'topic_like': $lable = Yii::t('app', 'Up Vote your topic'); break; case 'tweet_like': $lable = Yii::t('app', 'Up Vote your tweet'); break; case 'comment_like': $lable = Yii::t('app', 'Up Vote your reply'); break; case 'topic_mark_wiki': $lable = Yii::t('app', 'has mark your topic as wiki:'); break; case 'topic_mark_excellent': $lable = Yii::t('app', 'has recomended your topic:'); break; case 'comment_append': $lable = Yii::t('app', 'Commented topic has new update:'); break; case 'attention_append': $lable = Yii::t('app', 'Attented topic has new update:'); break; default: $lable = ''; break; } return $lable; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'from_user_id' => 'From User ID', 'user_id' => 'User ID', 'post_id' => 'Post ID', 'comment_id' => 'Comment ID', 'type' => 'Type', 'data' => 'Data', 'created_at' => 'Created At', 'updated_at' => 'Updated At', ]; } } ================================================ FILE: frontend/models/PasswordResetRequestForm.php ================================================ set('mailer', [ 'class' => 'yii\swiftmailer\Mailer', 'viewPath' => '@common/mail', 'transport' => [ 'class' => 'Swift_SmtpTransport', 'host' => Yii::$app->setting->get('smtpHost'), 'username' => Yii::$app->setting->get('smtpUser'), 'password' => Yii::$app->setting->get('smtpPassword'), 'port' => Yii::$app->setting->get('smtpPort'), // 'mail' => Yii::$app->setting->get('smtpMail'), // 显示地址 'encryption' => 'tls', ], ]); } /** * @inheritdoc */ public function rules() { return [ ['email', 'filter', 'filter' => 'trim'], ['email', 'required'], ['email', 'email'], ['email', 'exist', 'targetClass' => '\common\models\User', 'filter' => ['status' => User::STATUS_ACTIVE], 'message' => 'There is no user with such email.' ], ]; } /** * Sends an email with a link, for resetting the password. * * @return boolean whether the email was send */ public function sendEmail() { /* @var $user User */ $user = User::findOne([ 'status' => User::STATUS_ACTIVE, 'email' => $this->email, ]); if ($user) { if (!User::isPasswordResetTokenValid($user->password_reset_token)) { $user->generatePasswordResetToken(); } if ($user->save()) { return \Yii::$app->mailer->compose('passwordResetToken', ['user' => $user]) ->setFrom([\Yii::$app->setting->get('smtpUser') => \Yii::$app->name . ' robot']) ->setTo($this->email) ->setSubject('Password reset for ' . \Yii::$app->name) ->send(); } } return false; } } ================================================ FILE: frontend/models/ResetPasswordForm.php ================================================ _user = User::findByPasswordResetToken($token); if (!$this->_user) { throw new InvalidParamException('Wrong password reset token.'); } parent::__construct($config); } /** * @inheritdoc */ public function rules() { return [ ['password', 'required'], ['password', 'string', 'min' => 6], ]; } /** * Resets password. * * @return boolean if password was reset. */ public function resetPassword() { $user = $this->_user; $user->password = $this->password; $user->removePasswordResetToken(); return $user->save(); } } ================================================ FILE: frontend/models/SignupForm.php ================================================ 'trim'], ['username', 'required'], ['username', 'match', 'pattern' => '/^[a-z]\w*$/i', 'message' => '{attribute}只能为数字和字母'], ['username', 'unique', 'targetClass' => '\common\models\User', 'message' => '此{attribute}已经被使用'], ['username', 'string', 'min' => 4, 'max' => 12], ['email', 'filter', 'filter' => 'trim'], ['email', 'required'], ['email', 'email'], ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => '此{attribute}已经被使用'], ['password', 'required'], ['password', 'string', 'min' => 6], ['role', 'integer'], ['role', 'default', 'value' => User::ROLE_USER], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'username' => '用户名', 'password' => '密码', 'email' => '邮箱', 'role' => '角色', ]; } /** * Signs user up. * * @return User|null the saved model or null if saving fails */ public function signup() { if ($this->validate()) { $user = new User(); $user->username = $this->username; $user->email = $this->email; $user->role = $this->role; $user->setPassword($this->password); $user->generateAuthKey(); $user->save(); return $user; } return null; } } ================================================ FILE: frontend/modules/nav/Module.php ================================================ orderBy('order asc')->all(); return $this->render('index', ['nav' => $nav]); } } ================================================ FILE: frontend/modules/nav/views/default/index.php ================================================ title = '网站导航'; ?> ================================================ FILE: frontend/modules/topic/Module.php ================================================ [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ // 登录用户POST操作 ['allow' => true, 'actions' => ['delete'], 'verbs' => ['POST'], 'roles' => ['@']], // 登录用户才能操作 ['allow' => true, 'actions' => ['create', 'update'], 'roles' => ['@']], ] ], ]); } /** * 创建回复 * @param $id * @return PostComment|\yii\web\Response */ public function actionCreate($id) { $model = new PostComment(); if ($model->load(Yii::$app->request->post())) { $topService = new TopicService(); if (!$topService->filterContent($model->comment)) { $model->addError('comment', '回复内容请勿回复无意义的内容,如你想收藏或赞等功能,请直接操作这篇帖子。'); return $this->redirect(['/topic/default/view', 'id' => $id]); } $model->user_id = Yii::$app->user->id; $model->post_id = $id; $model->ip = Yii::$app->getRequest()->getUserIP(); if ($model->save()) { $this->flash("回复成功", 'success'); } else { $this->flash(array_values($model->getFirstErrors())[0], 'warning'); } return $this->redirect(['/topic/default/view', 'id' => $id]); } return $model; } /** * 修改回复 * @param $id * @return string|\yii\web\Response * @throws NotFoundHttpException */ public function actionUpdate($id) { $model = PostComment::findComment($id); if (!$model->isCurrent()) { throw new NotFoundHttpException(); } if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['/topic/default/view', 'id' => $model->post_id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * 伪删除 * @param $id * @return \yii\web\Response * @throws NotFoundHttpException */ public function actionDelete($id) { $model = PostComment::findComment($id); if (!$model->isCurrent()) { throw new NotFoundHttpException(); } // 事物 暂时数据库类型不支持 无效 $transaction = \Yii::$app->db->beginTransaction(); $updateComment = $model->updateCounters(['status' => -1]); $updateNotify = Notification::updateAll(['status' => 0], ['comment_id' => $model->id]); $updateTopic = Topic::updateAllCounters(['comment_count' => -1], ['id' => $model->post_id]); if ($updateNotify && $updateComment && $updateTopic) { $transaction->commit(); } else { $transaction->rollback(); } return $this->redirect(['/topic/default/view', 'id' => $model->post_id]); } } ================================================ FILE: frontend/modules/topic/controllers/DefaultController.php ================================================ '最新', 'excellent' => '优质', 'hotest' => '热门', // 'uncommented' => '未回答的' ]; public function behaviors() { return ArrayHelper::merge(parent::behaviors(), [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ // 默认只能Get方式访问 ['allow' => true, 'actions' => ['view', 'index', 'search', 'captcha'], 'verbs' => ['GET']], // 登录用户才能提交回复或其他内容 ['allow' => true, 'actions' => ['api', 'view', 'delete'], 'verbs' => ['POST'], 'roles' => ['@']], // 登录用户才能使用API操作(赞,踩,收藏) ['allow' => true, 'actions' => ['create', 'update', 'revoke', 'excellent'], 'roles' => ['@']], ] ], ]); } /** * 话题列表 * @return mixed */ public function actionIndex() { // 话题或者分类筛选 $params = Yii::$app->request->queryParams; $search = PostService::search($params); return $this->render('index', [ 'searchModel' => $search['searchModel'], 'sorts' => $this->sorts, 'dataProvider' => $search['dataProvider'], 'nodes' => PostMeta::getNodes(), ]); } public function actionSearch() { $keyword = htmlspecialchars(Yii::$app->request->get('keyword')); if (empty($keyword)) { $this->goHome(); } // 记录log $model = new SearchLog(); $model->setAttributes([ 'user_id' => (Yii::$app->user->isGuest) ? '' : Yii::$app->user->identity->getId(), 'keyword' => $keyword, 'created_at' => time(), ]); $model->save(); $id = ArrayHelper::getColumn(Search::search($keyword), 'topic_id') ?: 0; $search = PostService::search(['PostSearch' => ['id' => $id]]); return $this->render('index', [ 'searchModel' => $search['searchModel'], 'sorts' => $this->sorts, 'dataProvider' => $search['dataProvider'], ]); } /** * 话题详细页 * @param integer $id * @return mixed */ public function actionView($id) { $model = Topic::findTopic($id); //登录才能访问的节点内容 if (\Yii::$app->user->isGuest && in_array($model->category->alias, params('loginNode'))) { $this->flash('查看本主题需要登录!', 'warning'); return $this->redirect(['/site/login']); } $dataProvider = new ActiveDataProvider([ 'query' => PostComment::findCommentList($id), 'pagination' => [ 'pageSize' => self::PAGE_SIZE, ], 'sort' => ['defaultOrder' => ['created_at' => SORT_ASC]] ]); // 文章浏览次数 Topic::updateAllCounters(['view_count' => 1], ['id' => $id]); //内容页面打赏 if (in_array($model->category->alias, params('donateNode')) || array_intersect(explode(',', $model->tags), params('donateTag'))) { $donate = Donate::findOne(['user_id' => $model->user_id, 'status' => Donate::STATUS_ACTIVE]); } /** @var User $user */ $user = Yii::$app->user->identity; $admin = ($user && ($user->isAdmin($user->username) || $user->isSuperAdmin($user->username))) ? true : false; return $this->render('view', [ 'model' => $model, 'dataProvider' => $dataProvider, 'comment' => new PostComment(), 'admin' => $admin, 'donate' => isset($donate) ? $donate : [], ]); } /** * 新建话题 * @return mixed * @throws \yii\base\ExitException */ public function actionCreate() { $model = new Topic(); if ($time = $model->limitPostTime()) { $this->flash("发表文章失败!新注册用户只能回帖,{$time}秒之后才能发帖。", 'warning'); return $this->redirect('index'); } if ($time = $model->limitPostingIntervalTime()) { $this->flash("发表文章失败!请勿连续频繁发帖,{$time}秒之后才能发帖。", 'warning'); return $this->redirect('index'); } if ($model->load(Yii::$app->request->post()) && $model->validate()) { $topService = new TopicService(); if (!$topService->filterContent($model->title) || !$topService->filterContent($model->content)) { $model->addError('content', '请勿发表无意义的内容'); return $this->redirect('create'); } if ($model->save(false)) { $this->flash('发表文章成功!', 'success'); return $this->redirect(['view', 'id' => $model->id]); } } else { return $this->render('create', [ 'model' => $model, ]); } } /** * 修改自己的话题 * @param integer $id * @return string|\yii\web\Response * @throws NotFoundHttpException * @throws \yii\base\ExitException */ public function actionUpdate($id) { $model = Topic::findTopic($id); if (!($model && (User::getThrones() || $model->isCurrent()))) { throw new NotFoundHttpException; } if ($model->load(Yii::$app->request->post()) && $model->validate()) { if ($model->save()) { $this->flash('发表更新成功!', 'success'); return $this->redirect(['view', 'id' => $model->id]); } } else { return $this->render('update', [ 'model' => $model, ]); } } /** * 伪删除 * @param $id * @return \yii\web\Response * @throws NotFoundHttpException */ public function actionDelete($id) { $model = Topic::findTopic($id); if (!($model && (User::getThrones() || $model->isCurrent()))) { throw new NotFoundHttpException; } if ($model->comment_count) { $this->flash("「{$model->title}」此文章已有回复,属于共有财产,不能删除", 'warning'); } else { TopicService::delete($model); $revoke = Html::a('撤消', ['/topic/default/revoke', 'id' => $model->id]); $this->flash("「{$model->title}」文章删除成功。 反悔了?{$revoke}", 'success'); } return $this->redirect(['index']); } /** * 撤消删除 * @param $id * @return \yii\web\Response * @throws NotFoundHttpException */ public function actionRevoke($id) { $model = Topic::findDeletedTopic($id); if (!($model && (User::getThrones() || $model->isCurrent()))) { throw new NotFoundHttpException; } TopicService::revoke($model); $this->flash("「{$model->title}」文章撤销删除成功。", 'success'); return $this->redirect(['/topic/default/view', 'id' => $model->id]); } /** * 加精华 * @param $id * @return \yii\web\Response * @throws NotFoundHttpException */ public function actionExcellent($id) { /** @var User $user */ $user = Yii::$app->user->identity; $model = Topic::findTopic($id); if ($user && ($user->isAdmin($user->username) || $user->isSuperAdmin($user->username))) { TopicService::excellent($model); $this->flash("操作成功", 'success'); return $this->redirect(['/topic/default/view', 'id' => $model->id]); } else { throw new NotFoundHttpException(); } } } ================================================ FILE: frontend/modules/topic/models/Topic.php ================================================ * createTime : 15/4/19 下午 5:57 * description: */ namespace frontend\modules\topic\models; use common\models\Post; use common\models\PostTag; use common\models\Search; use common\models\UserInfo; use common\services\NotificationService; use common\services\TopicService; use frontend\modules\user\models\UserMeta; use Yii; use yii\helpers\Url; use yii\web\NotFoundHttpException; class Topic extends Post { const TYPE = 'topic'; /** * @var boolean CC 协议 */ public $cc; public function getLike() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'like', $this->id); } public function getFollow() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'follow', $this->id); } public function getHate() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'hate', $this->id); } public function getFavorite() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'favorite', $this->id); } public function getThanks() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'thanks', $this->id); } /** * 获取关注者 */ public function getFollower() { return $this->hasMany(UserMeta::className(), ['target_id' => 'id']) ->where(['target_type' => self::TYPE, 'type' => 'follow']); } /** * 通过 ID 获取指定话题 * @param $id * @param string $condition * @return array|null|\yii\db\ActiveRecord * @throws NotFoundHttpException */ public static function findModel($id, $condition = '') { // if (!($model = Yii::$app->cache->get('topic' . $id))) { $model = static::find() ->where($condition) ->andWhere(['id' => $id, 'type' => self::TYPE]) ->one(); // } if ($model) { // Yii::$app->cache->set('topic' . $id, $model, 0); return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } /** * 通过 ID 获取指定话题 * @param $id * @return array|Topic|null|\yii\db\ActiveRecord * @throws NotFoundHttpException */ public static function findTopic($id) { return static::findModel($id, ['>=', 'status', self::STATUS_ACTIVE]); } /** * 获取已经删除过的话题 * @param $id * @return array|Topic|null|\yii\db\ActiveRecord * @throws NotFoundHttpException */ public static function findDeletedTopic($id) { return static::findModel($id, ['>=', 'status', self::STATUS_DELETED]); } public $atUsers; public function beforeSave($insert) { if (parent::beforeSave($insert)) { if ($this->tags && is_array($this->tags)) { $this->addTags($this->tags); $this->tags = implode(',', $this->tags); } if (params('createPostNeedVerify')) { $this->status = self::STATUS_DELETED; } $username = Yii::$app->user->identity->username; $url = Url::to('/member/' . $username, true); $this->content = TopicService::contentTopic($this->content, $this) . ($this->cc ? t('app', 'cc {username} {url}', ['username' => $username, 'url' => $url]) : ''); if ($insert) { $this->user_id = (($this->user_id) ?: Yii::$app->user->id); $this->type = self::TYPE; $this->last_comment_time = $this->created_at; } return true; } else { return false; } } public function afterSave($insert, $changedAttributes) { parent::afterSave($insert, $changedAttributes); if (isset(Yii::$app->params['setting']) && Yii::$app->params['setting']['xunsearch']) { if ($insert) { $search = new Search(); $search->topic_id = $this->id; $search->status = self::STATUS_ACTIVE; } else { $search = Search::findOne($this->id); if (!$search) { // 如果立即修改 会因为在 xunsearch 找不到而不能 save return false; } $search->status = $this->status; } $search->title = $this->title; $search->content = $this->content; $search->updated_at = $this->updated_at; $search->save(); } (new NotificationService())->newPostNotify(\Yii::$app->user->identity, $this, $this->atUsers); if ($insert) { // 保存 meta data (new UserMeta)->saveNewMeta('topic', $this->id, 'follow'); // 更新个人总统计 UserInfo::updateAllCounters(['post_count' => 1], ['user_id' => $this->user_id]); } } /** * 最后回复更新 * @param string $username * @return bool */ public function lastCommentToUpdate($username = '') { $this->setAttributes([ 'last_comment_username' => $username, 'last_comment_time' => time() ]); return $this->save(); } /** * 添加标签 * @param array $tags * @return bool */ public function addTags(array $tags) { $return = false; $tagItem = new PostTag(); foreach ($tags as $tag) { $_tagItem = clone $tagItem; $tagRaw = $_tagItem::findOne(['name' => $tag]); if (!$tagRaw) { $_tagItem->setAttributes([ 'name' => $tag, 'count' => 1, ]); if ($_tagItem->save()) { $return = true; } } else { $tagRaw->updateCounters(['count' => 1]); } } return $return; } } ================================================ FILE: frontend/modules/topic/views/comment/_form.php ================================================ * createTime : 15/4/20 下午9:15 * description: */ use common\widgets\JsBlock; use yii\helpers\Html; use yii\helpers\Url; use yii\widgets\ActiveForm; use yiier\editor\EditorMdWidget; \frontend\assets\AtJsAsset::register($this); ?>
user->isGuest): ?>
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册
[ $model->isNewRecord ? '/topic/comment/create' : '/topic/comment/update', 'id' => Yii::$app->request->getQueryParam('id')], 'fieldConfig' => [ 'template' => "{input}\n{hint}\n{error}" ] ]); ?> field($model, 'comment')->widget(EditorMdWidget::className(), [ 'clientOptions' => [ 'height' => 200, 'imageUpload' => true, 'autoFocus' => false, 'placeholder' => '请尽量让自己的回复能够对别人有帮助', 'imageUploadURL' => Url::to(['/site/upload', 'field' => 'editormd-image-file']), ] ]) ?>
isNewRecord ? '创建回复' : '修改回复', [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary', ] ) ?>
'_blank']) ?>
\yii\web\View::POS_READY]) ?>
================================================ FILE: frontend/modules/topic/views/comment/_item.php ================================================ * createTime : 15/4/20 下午9:56 * description: */ use yii\helpers\Html; use yii\helpers\HtmlPurifier; use yii\helpers\Markdown; /** @var \common\models\PostComment $model */ $index += +1 + $widget->dataProvider->pagination->page * $widget->dataProvider->pagination->pageSize; ?> status): ?>
楼 已删除.
user->userAvatar, ['class' => 'media-object avatar-48 img-circle']), ['/user/default/show', 'username' => $model->user['username']] ); ?>
user['username'], ['/user/default/show', 'username' => $model->user['username']], ['class' => 'author']), '•', Html::a("#{$index}", "#comment{$index}", ['class' => 'comment-floor']), '•', Html::tag('addr', Yii::$app->formatter->asRelativeTime($model->created_at), ['title' => Yii::$app->formatter->asDatetime($model->created_at)]); ?> isCurrent()) { echo Html::a( Html::tag('i', '', ['class' => 'fa fa-thumbs-o-up']) . ' ' . Html::tag('span', $model->like_count) . ' 个赞', 'javascript:;' ); echo Html::a('', ['/topic/comment/update', 'id' => $model->id], ['title' => '修改回帖', 'class' => 'fa fa-pencil'] ); echo Html::a('', ['/topic/comment/delete', 'id' => $model->id], [ 'title' => '删除回复', 'class' => 'fa fa-trash', 'data' => [ 'confirm' => "您确认要删除回复吗?", 'method' => 'post', ], ] ); } else { echo Html::a( Html::tag('i', '', ['class' => 'fa fa-thumbs-o-up']) . ' ' . Html::tag('span', $model->like_count) . ' 个赞', '#', [ 'data-do' => 'like', 'data-id' => $model->id, 'data-type' => 'comment', 'class' => ($model->like) ? 'active' : '' ] ); echo Html::a('', '#', [ 'data-username' => $model->user['username'], 'data-floor' => $index, 'title' => '回复此楼', 'class' => 'fa fa-mail-reply btn-reply' ] ); } ?>
comment, 'gfm')) ?>
================================================ FILE: frontend/modules/topic/views/comment/create.php ================================================ * createTime : 15/4/20 下午9:17 * description: */ ?>
添加回复 user->getIsGuest()): ?> (需要登录)
render('_form', [ 'model' => $model, ]) ?>
================================================ FILE: frontend/modules/topic/views/comment/index.php ================================================ * createTime : 15/4/20 下午9:16 * description: */ use yii\helpers\Html; ?>
comment_count) ?> tags): ?> $value){ echo Html::a(Html::encode($value), ['/topic/default/index', 'tag' => $value], ['class' => 'btn btn-xs btn-primary'] ), ' ';} ?>
$dataProvider, 'itemOptions' => ['class' => 'list-group-item media mt0'], 'summary' => false, 'itemView' => '_item', ]) ?>
================================================ FILE: frontend/modules/topic/views/comment/update.php ================================================ * createTime : 15/4/20 下午9:15 * description: */ use yii\helpers\Html; /** @var \common\models\PostComment $model */ $this->title = Yii::t('app', '更新回复: ') . ' ' . $model->post->title; ?>
post->title, ['/topic/default/view', 'id' => $model->post_id]) ?>
render('_form', [ 'model' => $model, ]) ?>
================================================ FILE: frontend/modules/topic/views/default/_form.php ================================================ tags = $model->tags ? explode(',', $model->tags) : ''; ?>
[ 'autocomplete' => 'off' ], 'fieldConfig' => [ 'template' => "{input}\n{hint}\n{error}" ] ]); ?> errorSummary($model, [ 'class' => 'alert alert-danger' ]) ?> field($model, 'title')->textInput([ 'maxlength' => 255, 'placeholder' => '标题' ]) ?> field($model, 'post_meta_id')->widget(Select2Widget::classname(), [ 'items' => ['' => '选择一个分类'] + \common\models\PostMeta::topicCategory(), ]); ?> field($model, 'content')->widget(EditorMdWidget::className(), [ 'options' => ['id' => 'sss'], 'clientOptions' => [ 'placeholder' => '鼓励分享和讨论不鼓励伸手提问党。真的想提问请在提问帖内附上你曾经为了解决问题付出的行动或者分享一段小知识。', 'imageUpload' => true, 'autoFocus' => false, 'imageUploadURL' => Url::to(['/site/upload', 'field' => 'editormd-image-file']), ] ]) ?> field($model, 'tags')->widget(Select2Widget::classname(), [ 'options' => [ 'multiple' => true, 'placeholder' => '标签(可选)' ], 'settings' => ['width' => '100%'], 'items' => ArrayHelper::map(PostTag::find()->orderBy('count')->asArray()->all(), 'name', 'name'), ]); ?>
field($model, 'cc')->checkbox() ?>
isNewRecord ? '创建话题' : '修改话题', [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary', ] ) ?>
'_blank']) ?>
================================================ FILE: frontend/modules/topic/views/default/_item.php ================================================
'badge badge-reply-count']), ['/topic/default/view', 'id' => $model->id, '#' => 'comment' . $model['comment_count']], ['class' => 'pull-right'] ); ?>
user, 'userAvatar'), ['class' => 'media-object img-circle']), ['/user/default/show', 'username' => $model->user['username']] ); ?>
title), ['/topic/default/view', 'id' => $model->id], ['title' => $model->title] ); ?> status == Topic::STATUS_EXCELLENT) ? Html::tag('i', '', ['class' => 'fa fa-trophy excellent']) : null ?>
like_count) { echo Html::a(Html::tag('span', ' ' . $model->like_count . ' ', ['class' => 'fa fa-thumbs-o-up']), ['/topic/default/view', 'id' => $model->id], ['class' => 'remove-padding-left'] ), ' • '; } if (!request('node') && isset($model->category->name)) { echo Html::a( $model->category->name, ['/topic/default/index', 'node' => $model->category->alias], ['class' => 'node'] ), ' • '; } if ($model->last_comment_username) { echo Html::tag('span', Yii::t('frontend', 'last_by') . Html::a( Html::tag('strong', ' ' . $model->last_comment_username . ' '), ['/user/default/show', 'username' => $model->last_comment_username]) . Yii::t('frontend', 'reply_at {datetime}', [ 'datetime' => Formatter::relative($model->last_comment_time) ]) ); } else { echo '由', Html::a( Html::tag('strong', ' ' . $model->user['username'] . ' '), ['/user/default/show', 'username' => $model->user['username']] ), Html::tag('span', Yii::t('frontend', 'created_at {datetime}', [ 'datetime' => Formatter::relative($model->created_at) ]) ); } echo ' • ' . $model->view_count . ' 次阅读' ?>
================================================ FILE: frontend/modules/topic/views/default/create.php ================================================ title = '发布新话题'; // $this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']]; // $this->params['breadcrumbs'][] = $this->title; ?>
title ?>
render('_form', [ 'model' => $model, ]) ?>
'create' ])?> ================================================ FILE: frontend/modules/topic/views/default/index.php ================================================ title = '社区'; $sort = Yii::$app->request->getQueryParam('sort'); if ($node = Yii::$app->request->getQueryParam('node')) { $node = \common\models\PostMeta::find()->where(['alias' => $node])->one(); } /** @var array $sorts */ /** @var \common\models\PostMeta $node */ /** @var \common\models\PostMeta[] $nodes */ /** @var \yii\data\ActiveDataProvider $dataProvider */ ?>
name ?> 共有 getTotalCount() ?> 个讨论主题

description ?>

request('tab') ? null : 'active']) ?> $value): ?> name, ['/topic/default/index', 'tab' => $value->alias], ['class' => request('tab') == $value->alias ? 'active' : null]) ?>
排序: $name): ?> $key]), ['class' => ($sort == $key || ((empty($sort) && $key == 'newest'))) ? 'active' : '']) ?> \
'pull-left']); } elseif (request('keyword')) { echo Html::tag('div', '搜索:' . htmlspecialchars(request('keyword')), ['class' => 'pull-left']); } elseif (request('tab')) { /** @var \common\models\PostMeta $node */ if ($node = \yii\helpers\ArrayHelper::getValue($nodes, request('tab'))) { foreach ($node->children as $item) { $active = request('node') == $item->alias ? 'active' : null; echo Html::a($item->name, ['/topic/default/index', 'node' => $item->alias], ['class' => "children-node " . $active]); } } } ?>
0, 'formSelector' => false, 'linkSelector' => '.pagination a' ]); ?> $dataProvider, 'itemOptions' => ['class' => 'list-group-item'], 'summary' => false, 'itemView' => '_item', 'options' => ['class' => 'list-group'], ]) ?>
$node ]); ?> ================================================ FILE: frontend/modules/topic/views/default/update.php ================================================ title = Yii::t('app', 'Update Post: ') . ' ' . $model->title; ?>
title) ?>
render('_form', [ 'model' => $model, ]) ?>
'create' ])?> ================================================ FILE: frontend/modules/topic/views/default/view.php ================================================ title = $model->title; ?>
title), ['class' => 'media-heading']); ?>
category->name, ['/topic/default/index', 'node' => $model->category->alias], ['class' => 'node'] ) ?> · user['username'], ['/user/default/show', 'username' => $model->user['username']]) ?> · 于 formatter->asRelativeTime($model->created_at), ['title' => Yii::$app->formatter->asDatetime($model->created_at)]) ?> 发布 · view_count ?> 次阅读
user->userAvatar, ['class' => 'media-object avatar-48 img-circle']), ['/user/default/show', 'username' => $model->user['username']] ); ?>
content, 'gfm')) ?>
'btn btn-danger', 'id' => 'donate-btn']) ?>

description ?>

status == 2): ?>
本帖已被设为精华帖!
render( '@frontend/modules/topic/views/comment/index', ['model' => $model, 'dataProvider' => $dataProvider] ) ?> render( '@frontend/modules/topic/views/comment/create', ['model' => $comment, 'post' => $model] ) ?>
'view', 'node' => $model->category, 'tags' => $model->tags ]); ?> ================================================ FILE: frontend/modules/tweet/Module.php ================================================ [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ // 默认只能Get方式访问 ['allow' => true, 'actions' => ['index'], 'verbs' => ['GET']], // 登录用户POST操作 ['allow' => true, 'actions' => ['delete'], 'verbs' => ['POST'], 'roles' => ['@']], // 登录用户才能操作 ['allow' => true, 'actions' => ['create'], 'roles' => ['@']], ] ], ]); } /** * @return string * @throws \yii\base\ExitException */ public function actionIndex() { $searchModel = new TweetSearch(); $params = Yii::$app->request->queryParams; $params['TweetSearch']['content'] = empty($params['topic']) ? '' : $params['topic']; $dataProvider = $searchModel->search($params); $dataProvider->query->andWhere([ Post::tableName() . '.type' => Tweet::TYPE, 'status' => [Post::STATUS_ACTIVE, Post::STATUS_EXCELLENT] ]); $model = new Tweet(); if ($time = $model->limitPostTime()) { $this->flash("新注册用户只能回帖,{$time}秒之后才能发帖。", 'warning'); } return $this->render('index', [ 'model' => $model, 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * 新建动弹 * @return mixed * @throws \yii\base\ExitException */ public function actionCreate() { $model = new Tweet(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { $topService = new TweetService(); if (!$topService->filterContent($model->content)) { $model->addError('content', '请勿发表无意义的内容'); return $this->redirect('index'); } $model->user_id = Yii::$app->user->id; $model->type = $model::TYPE; if ($model->save()) { $this->flash('发表成功!', 'success'); } } if ($model->hasErrors()) { $this->flash('发表失败!' . array_values($model->firstErrors)[0], 'error'); } return $this->redirect('index'); } /** * 伪删除 * @param $id * @return \yii\web\Response * @throws NotFoundHttpException * @throws \yii\base\ExitException */ public function actionDelete($id) { /** @var Tweet $model */ $model = Tweet::findTweet($id); if (!$model->isCurrent()) { throw new NotFoundHttpException(); } if ($model->comment_count) { $model->addError('content', '已有回复,属于共有财产,不能删除'); } else { TweetService::delete($model); $this->flash("删除成功。 ", 'success'); } return $this->redirect(['index']); } } ================================================ FILE: frontend/modules/tweet/models/Tweet.php ================================================ * createTime : 15/4/19 下午5:57 * description: */ namespace frontend\modules\tweet\models; use common\models\Post; use common\services\NotificationService; use common\services\PostService; use frontend\modules\user\models\UserMeta; use Yii; use yii\web\NotFoundHttpException; class Tweet extends Post { const TYPE = 'tweet'; public function getLike() { $model = new UserMeta(); return $model->isUserAction(self::TYPE, 'like', $this->id); } /** * @inheritdoc */ public function rules() { return [ [['content'], 'required'], ['content', 'validateLimitPostTime'], [ [ 'post_meta_id', 'user_id', 'view_count', 'comment_count', 'favorite_count', 'like_count', 'thanks_count', 'hate_count', 'status', 'order', 'created_at', 'updated_at' ], 'integer' ], [['content'], 'string', 'min' => 3, 'max' => 500], [['post_meta_id'], 'default', 'value' => 0], [['title'], 'default', 'value' => ''], ]; } /** * 通过ID获取指定话题 * @param $id * @param string $condition * @return array|null|\yii\db\ActiveRecord|static * @throws NotFoundHttpException */ public static function findModel($id, $condition = '') { if (!$model = Yii::$app->cache->get('topic' . $id)) { $model = static::find() ->where($condition) ->andWhere(['id' => $id, 'type' => self::TYPE]) ->one(); } if ($model) { Yii::$app->cache->set('topic' . $id, $model, 0); return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } /** * 通过ID获取指定动弹 * @param $id * @return array|Topic|null|\yii\db\ActiveRecord * @throws NotFoundHttpException */ public static function findTweet($id) { return static::findModel($id, ['>=', 'status', self::STATUS_ACTIVE]); } /** * 获取已经删除过的动弹 * @param $id * @return array|null|\yii\db\ActiveRecord * @throws NotFoundHttpException */ public static function findDeletedTweet($id) { return static::findModel($id, ['>=', 'status', self::STATUS_DELETED]); } public $atUsers; public function beforeSave($insert) { if (!parent::beforeSave($insert)) { return false; } $this->content = PostService::contentTweet($this->content, $this); return true; } public function afterSave($insert, $changedAttributes) { parent::afterSave($insert, $changedAttributes); (new UserMeta())->saveNewMeta($this->type, $this->id, 'follow'); (new NotificationService())->newPostNotify(\Yii::$app->user->identity, $this, $this->atUsers); } } ================================================ FILE: frontend/modules/tweet/models/TweetSearch.php ================================================ with('user'); $dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => [ 'pageSize' => 20, ], 'sort' => ['defaultOrder' => [ 'order' => SORT_ASC, 'updated_at' => SORT_DESC, ]] ]); if (!($this->load($params) && $this->validate())) { return $dataProvider; } $query->andFilterWhere([ 'id' => $this->id, 'post_meta_id' => $this->post_meta_id, 'user_id' => $this->user_id, 'view_count' => $this->view_count, 'comment_count' => $this->comment_count, 'favorite_count' => $this->favorite_count, 'like_count' => $this->like_count, 'thanks_count' => $this->thanks_count, 'hate_count' => $this->hate_count, 'status' => $this->status, 'order' => $this->order, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'type', $this->type]) ->andFilterWhere(['like', 'title', $this->title]) ->andFilterWhere(['like', 'author', $this->author]) ->andFilterWhere(['like', 'excerpt', $this->excerpt]) ->andFilterWhere(['like', 'image', $this->image]) ->andFilterWhere(['like', 'content', $this->content]) ->andFilterWhere(['like', 'tags', $this->tags]); return $dataProvider; } } ================================================ FILE: frontend/modules/tweet/views/default/_form.php ================================================
'/tweet/default/create', 'fieldConfig' => [ 'template' => "{input}\n{hint}\n{error}" ] ]); ?> field($model, 'content', [ 'selectors' => [ 'input' => '#md-input' ], ])->textarea([ 'placeholder' => t('app', 'Tweet Content'), 'id' => 'md-input', 'data-at-topic' => true, 'rows' => 5 ]) ?>
$model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
'_blank']) ?>
================================================ FILE: frontend/modules/tweet/views/default/_item.php ================================================
user->userAvatar, ['class' => 'media-object']), ['/user/default/show', 'username' => $model->user['username']] ); ?>
user['username'], ['/user/default/show', 'username' => $model->user['username']] ), '•', Html::tag('span', \common\helpers\Formatter::relative($model->updated_at)); ?>
content, 'gfm')) ?>
isCurrent()) { echo Html::a( Html::tag('i', '', ['class' => 'fa fa-thumbs-o-up']) . ' ' . Html::tag('span', $model->like_count . ' '), 'javascript:;' ); if ($model->comment_count == 0) { echo Html::a( Html::tag('i', '', ['class' => 'fa fa-trash']) . ' 删除', ['/tweet/default/delete', 'id' => $model->id], [ 'data' => [ 'confirm' => "您确认要删除吗?", 'method' => 'post', ], ] ); } } else { echo Html::a( Html::tag('i', '', ['class' => 'fa fa-thumbs-o-up']) . ' ' . Html::tag('span', $model->like_count . ' '), '#', [ 'data-do' => 'like', 'data-id' => $model->id, 'data-type' => $model->type, 'class' => ($model->like) ? 'active' : '' ] ); } ?>
================================================ FILE: frontend/modules/tweet/views/default/index.php ================================================ title = '发布新动弹'; /** @var \frontend\modules\tweet\models\Tweet $model*/ /** @var \yii\data\ActiveDataProvider $dataProvider*/ ?>
title ?> 500
render('_form', [ 'model' => $model, ]) ?>
$dataProvider, 'itemView' => '_item', 'itemOptions' => ['class' => 'list-group-item item'], 'summary' => false, // 'options' => ['class' => ''], 'pager' => [ 'class' => \kop\y2sp\ScrollPager::className(), 'eventOnRendered' => "function() { emojify.run(); $('pre code').each(function (i, block) { hljs.highlightBlock(block); }); }", 'triggerOffset' => 5 ] ]); ?>
'create' ]) ?> ================================================ FILE: frontend/modules/user/Module.php ================================================ * createTime : 15/4/19 下午3:11 * description: */ namespace frontend\modules\user\controllers; use common\services\CommentService; use common\services\TopicService; use common\services\TweetService; use Yii; use common\components\Controller; use yii\web\NotFoundHttpException; class ActionController extends Controller { public function beforeAction($action) { if (Yii::$app->user->isGuest) { Yii::$app->getResponse()->redirect(\Yii::$app->getUser()->loginUrl)->send(); } return parent::beforeAction($action); } /** * 赞话题和回复 * @param $type * @param $id * @return array|string * @throws NotFoundHttpException */ public function actionLike($type, $id) { switch ($type) { case 'topic': $topicService = new TopicService(); list($result, $data) = $topicService->userDoAction($id, 'like'); break; case 'tweet': $tweetService = new TweetService(); list($result, $data) = $tweetService->userDoAction($id, 'like'); break; case 'comment': $commentService = new CommentService(); list($result, $data) = $commentService->userDoAction($id, 'like'); break; default: throw new NotFoundHttpException(); break; } if ($result) { return $this->message('提交成功!', 'success'); } else { return $this->message($data ? $data->getErrors() : '提交失败!'); } } /** * 喝倒彩话题 * @param $type * @param $id * @return array|string */ public function actionHate($type, $id) { if ($type == 'topic') { $topicService = new TopicService(); list($result, $data) = $topicService->userDoAction($id, 'hate'); if ($result) { return $this->message('提交成功!', 'success'); } else { return $this->message($data ? $data->getErrors() : '提交失败!'); } } } /** * 关注话题 * @param $type * @param $id * @return array|string */ public function actionFollow($type, $id) { if ($type == 'topic') { $topicService = new TopicService(); list($result, $data) = $topicService->userDoAction($id, 'follow'); if ($result) { return $this->message('提交成功!', 'success'); } else { return $this->message($data ? $data->getErrors() : '提交失败!'); } } } /** * 感谢话题 * @param $type * @param $id * @return array|string */ public function actionThanks($type, $id) { if ($type == 'topic') { $topicService = new TopicService(); list($result, $data) = $topicService->userDoAction($id, 'thanks'); if ($result) { return $this->message('提交成功!', 'success'); } else { return $this->message($data ? $data->getErrors() : '提交失败!'); } } } /** * 收藏话题 * @param $type * @param $id * @return array|string */ public function actionFavorite($type, $id) { if ($type == 'topic') { $topicService = new TopicService(); list($result, $data) = $topicService->userDoAction($id, 'favorite'); if ($result) { return $this->message('提交成功!', 'success'); } else { return $this->message($data ? $data->getErrors() : '提交失败!'); } } } } ================================================ FILE: frontend/modules/user/controllers/DefaultController.php ================================================ redirect(['show', 'username' => \Yii::$app->user->identity->username]); } /** * Shows user's profile. * @param string $username * @return \yii\web\Response * @throws \yii\web\NotFoundHttpException */ public function actionShow($username = '') { $user = $this->user($username); // 个人主页浏览次数 $currentUserId = \Yii::$app->getUser()->getId(); if (null != $currentUserId && $user->id != $currentUserId ) { UserInfo::updateAllCounters(['view_count' => 1], ['user_id' => $user->id]); } return $this->render('show', [ 'user' => $user, 'dataProvider' => $this->comment($user->id), ]); } protected function comment($userId) { return new ActiveDataProvider([ 'query' => PostComment::find()->where(['user_id' => $userId, 'status' => 1])->orderBy(['created_at' => SORT_DESC]), ]); } /** * 最近主题 * @param string $username * @return string * @throws NotFoundHttpException */ public function actionPost($username = '') { $user = $this->user($username); $dataProvider = new ActiveDataProvider([ 'query' => Topic::find() ->where(['user_id' => $user->id, 'type' => Topic::TYPE]) ->andWhere('status > :status ', [':status' => Topic::STATUS_DELETED]) ->orderBy(['created_at' => SORT_DESC]), ]); return $this->render('show', [ 'user' => $user, 'dataProvider' => $dataProvider, ]); } /** * 最新收藏 * @param string $username * @return string * @throws NotFoundHttpException */ public function actionFavorite($username = '') { $user = $this->user($username); return $this->render('show', [ 'user' => $user, 'dataProvider' => $this->userMeta($user->id, 'favorite'), ]); } public function actionPoint($username = '') { $user = $this->user($username); $dataProvider = new ActiveDataProvider([ 'query' => MeritLog::find()->where([ 'user_id' => $user->id, 'type' => 1, ])->orderBy(['created_at' => SORT_DESC]) ]); return $this->render('show', [ 'user' => $user, 'dataProvider' => $dataProvider, ]); } /** * @param $username * @return string */ public function actionLike($username) { $user = $this->user($username); return $this->render('show', [ 'user' => $user, 'dataProvider' => $this->userMeta($user->id, 'like'), ]); } /** * @param $userId * @param $type * @param string $targetType * @return ActiveDataProvider */ protected function userMeta($userId, $type, $targetType = 'topic') { return new ActiveDataProvider([ 'query' => UserMeta::find()->where([ 'user_id' => $userId, 'type' => $type, 'target_type' => $targetType, ])->orderBy(['created_at' => SORT_DESC]) ]); } protected function user($username = '') { $user = User::findOne(['username' => $username]); if ($user === null) { throw new NotFoundHttpException; } return $user; } } ================================================ FILE: frontend/modules/user/controllers/SecurityController.php ================================================ [ 'class' => AccessControl::className(), 'rules' => [ ['allow' => true, 'actions' => ['login', 'auth'], 'roles' => ['?']], ['allow' => true, 'actions' => ['logout'], 'roles' => ['@']], ] ], 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'logout' => ['post'] ] ] ]); } public function init() { parent::init(); \Yii::$app->set('authClientCollection', [ 'class' => 'yii\authclient\Collection', 'clients' => [ // 'google' => [ // 'class' => 'yii\authclient\clients\GoogleOAuth', // 'clientId' => \Yii::$app->setting->get('googleClientId'), // 'clientSecret' => \Yii::$app->setting->get('googleClientSecret'), // ], 'github' => [ 'class' => 'yii\authclient\clients\GitHub', 'clientId' => \Yii::$app->setting->get('githubClientId'), 'clientSecret' => \Yii::$app->setting->get('githubClientSecret'), ], ], ]); } /** @inheritdoc */ public function actions() { return [ 'auth' => [ 'class' => 'yii\authclient\AuthAction', 'successCallback' => [$this, 'authenticate'], ] ]; } /** * Logs the user in if this social account has been already used. Otherwise shows registration form. * @param ClientInterface $client * @return \yii\web\Response */ public function authenticate(ClientInterface $client) { $attributes = $client->getUserAttributes(); $provider = $client->getId(); $clientId = $attributes['id']; $account = UserAccount::find()->where([ 'provider' => $provider, 'client_id' => $clientId ])->one(); if ($account === null) { $account = \Yii::createObject([ 'class' => UserAccount::className(), 'provider' => $provider, 'client_id' => $clientId, 'data' => json_encode($attributes), 'created_at' => time() ]); $account->save(false); } if (null === ($user = $account->user)) { $this->action->successUrl = Url::to(['/site/connect', 'account_id' => $account->id]); } else { \Yii::$app->user->login($user, 1209600); // two weeks } } } ================================================ FILE: frontend/modules/user/controllers/SettingController.php ================================================ [ 'class' => VerbFilter::className(), 'actions' => [ 'disconnect' => ['post'] ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'allow' => true, 'actions' => ['profile', 'account', 'avatar', 'confirm', 'networks', 'connect', 'disconnect', 'donate'], 'roles' => ['@'] ], ] ], ]); } public function init() { parent::init(); Yii::$app->set('authClientCollection', [ 'class' => 'yii\authclient\Collection', 'clients' => [ 'google' => [ 'class' => 'yii\authclient\clients\Google', 'clientId' => Yii::$app->setting->get('googleClientId'), 'clientSecret' => Yii::$app->setting->get('googleClientSecret'), ], 'github' => [ 'class' => 'yii\authclient\clients\GitHub', 'clientId' => Yii::$app->setting->get('githubClientId'), 'clientSecret' => Yii::$app->setting->get('githubClientSecret'), ], ], ]); } /** @inheritdoc */ public function actions() { return [ 'connect' => [ 'class' => 'yii\authclient\AuthAction', 'successCallback' => [$this, 'connect'], ] ]; } /** * 修改个人资料 * @return mixed */ public function actionProfile() { /** @var UserInfo $model */ $model = UserInfo::findOne(['user_id' => Yii::$app->user->id]); if ($model->load(Yii::$app->request->post()) && $model->save()) { $this->flash('更新成功', 'success'); return $this->refresh(); } return $this->render('profile', [ 'model' => $model, ]); } /** * Displays a single User model. * @return string|Response * @throws \yii\base\InvalidConfigException */ public function actionAccount() { /** @var AccountForm $model */ $model = Yii::createObject(AccountForm::className()); $this->performAjaxValidation($model); if ($model->load(Yii::$app->request->post()) && $model->save()) { Yii::$app->session->setFlash('success', '您的用户信息修改成功'); return $this->refresh(); } return $this->render('account', [ 'model' => $model, ]); } /** * 头像设置 * @return mixed */ public function actionAvatar() { /** @var AvatarForm $model */ $model = Yii::createObject(AvatarForm::className()); if ($model->load(Yii::$app->request->post())) { if ($model->user->avatar) { // 删除头像 $model->deleteImage(); } $image = $model->uploadImage(); $hasError = true; if ($image !== false) { $path = $model->getNewUploadedImageFile(); if ($image->saveAs($path)) { $hasError = false; } } if ($hasError) { $model->useDefaultImage(); } if ($model->save() === false) { $hasError = true; } if ($hasError) { Yii::$app->session->setFlash('error', '您的头像更新失败'); } else { Yii::$app->session->setFlash('success', '您的用户信息修改成功'); } return $this->refresh(); } return $this->render('avatar', [ 'model' => $model, ]); } /** * 打赏设置 * @return mixed */ public function actionDonate() { /** @var Donate $model */ $model = Donate::findOne(['user_id' => Yii::$app->user->id]) ?: new Donate(['scenario' => 'create']); $oldQrCode = $model->qr_code; $model->description ?: $model->description = '如果这篇文章对您有帮助,不妨微信小额赞助我一下,让我有动力继续写出高质量的教程。'; if ($model->load(Yii::$app->request->post())) { if ($image = $model->uploadImage()) { \yii\helpers\FileHelper::createDirectory(\Yii::$app->basePath . \Yii::$app->params['qrCodePath']); $model->deleteImage(); $image->saveAs(\Yii::$app->basePath . \Yii::$app->params['qrCodePath'] . $model->qr_code); } if ($image === false && !empty($oldQrCode)) { $model->qr_code = $oldQrCode; } $model->user_id = Yii::$app->user->id; if ($model->save()) { Yii::$app->session->setFlash('success', '您的打赏信息修改成功'); } else { Yii::$app->session->setFlash('error', '您的打赏信息更新失败'); } return $this->refresh(); } return $this->render('donate', [ 'model' => $model, ]); } /** * 第三方账号绑定 * @return mixed */ public function actionNetworks() { return $this->render('networks', [ 'user' => Yii::$app->user->identity ]); } /** * 解除绑定第三方账号 * @param $id * @return Response * @throws ForbiddenHttpException * @throws NotFoundHttpException */ public function actionDisconnect($id) { /** @var UserAccount $account */ $account = UserAccount::findOne(['id' => $id]); if ($account === null) { throw new NotFoundHttpException; } if ($account->user_id != \Yii::$app->user->id) { throw new ForbiddenHttpException; } $account->delete(); return $this->redirect(['networks']); } /** * 绑定第三方账号 * @param ClientInterface $client * @return \yii\web\Response */ public function connect(ClientInterface $client) { $attributes = $client->getUserAttributes(); $provider = $client->getId(); $clientId = $attributes['id']; $account = UserAccount::find()->where([ 'provider' => $provider, 'client_id' => $clientId ])->one(); if ($account === null) { $account = Yii::createObject([ 'class' => UserAccount::className(), 'provider' => $provider, 'client_id' => $clientId, 'data' => json_encode($attributes), 'user_id' => Yii::$app->user->id, 'created_at' => time(), ]); $account->save(false); Yii::$app->session->setFlash('success', '账号绑定成功'); } else { Yii::$app->session->setFlash('error', '绑定失败,此账号已经绑定过了'); } $this->action->successUrl = Url::to(['/user/setting/networks']); } /** * Performs ajax validation. * @param AccountForm $model * @throws \yii\base\ExitException */ protected function performAjaxValidation($model) { if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) { Yii::$app->response->format = Response::FORMAT_JSON; Yii::$app->response->data = ActiveForm::validate($model); Yii::$app->response->send(); Yii::$app->end(); } } } ================================================ FILE: frontend/modules/user/models/AccountForm.php ================================================ _user == null) { $this->_user = \Yii::$app->user->identity; } return $this->_user; } /** @inheritdoc */ public function __construct(Mailer $mailer, $config = []) { $this->mailer = $mailer; $this->module = \Yii::$app->getModule('user'); $this->setAttributes([ 'username' => $this->user->username, 'email' => $this->user->email, 'tagline' => $this->user->tagline, ], false); parent::__construct($config); } /** @inheritdoc */ public function rules() { return [ [['username', 'email', 'current_password'], 'required'], [['username', 'email'], 'filter', 'filter' => 'trim'], ['username', 'match', 'pattern' => '/^[a-zA-Z]\w+$/'], ['username', 'string', 'min' => 3, 'max' => 20], ['email', 'email'], [['email', 'username'], 'unique', 'when' => function ($model, $attribute) { return $this->user->$attribute != $model->$attribute; }, 'targetClass' => '\common\models\User', 'message' => '此{attribute}已经被使用。'], ['new_password', 'string', 'min' => 6], ['tagline', 'string', 'max' => 40], ['current_password', function ($attr) { if (!\Yii::$app->security->validatePassword($this->$attr, $this->user->password_hash)) { $this->addError($attr, '当前密码是输入错误'); } }] ]; } /** @inheritdoc */ public function attributeLabels() { return [ 'email' => 'Email', 'username' => '用户名', 'new_password' => '新密码', 'tagline' => '一句话介绍', 'current_password' => '当前密码' ]; } /** @inheritdoc */ public function formName() { return 'settings-form'; } /** * Saves new account settings. * * @return bool */ public function save() { if ($this->validate()) { $this->user->username = $this->username; // 新密码没填写 则为不修改密码 ($this->new_password) ? $this->user->password = $this->new_password : ''; $this->user->tagline = $this->tagline; $this->user->email = $this->email; return $this->user->save(); } return false; } } ================================================ FILE: frontend/modules/user/models/AvatarForm.php ================================================ _user == null) { $this->_user = \Yii::$app->user->identity; } return $this->_user; } /** @inheritdoc */ public function rules() { return [ [['avatar'], 'required'], [['avatar'], 'image', 'extensions' => 'jpg, png, gif, jpeg', 'maxSize' => 1024 * 1024 * 2, 'tooBig' => \Yii::t('app', 'File has to be smaller than 2MB')], ]; } /** @inheritdoc */ public function attributeLabels() { return [ 'avatar' => '上传头像', ]; } /** * Saves new account settings. * * @return bool */ public function save() { if ($this->validate()) { $this->user->avatar = $this->avatar; return $this->user->save(); } return false; } /** * fetch stored image file name with complete path * @return string */ public function getImageFile() { return isset($this->user->avatar) ? \Yii::$app->basePath . \Yii::$app->params['avatarPath'] . $this->user->avatar : null; } /** * fetch new generated image file name with complete path * @return string */ public function getNewUploadedImageFile() { $uploadAvatarPath = \Yii::$app->basePath . \Yii::$app->params['avatarPath']; FileHelper::createDirectory($uploadAvatarPath); // 创建文件夹 return isset($this->avatar) ? $uploadAvatarPath . $this->avatar : null; } /** * use a default icon in case of errors while processing uploaded files */ public function useDefaultImage() { $identicon = new \Identicon\Identicon(); $this->avatar = $identicon->getImageDataUri($this->user->email); } /** * Process upload of image * * @return mixed the uploaded image instance */ public function uploadImage() { // get the uploaded file instance. for multiple file uploads // the following data will return an array (you may need to use // getInstances method) $image = UploadedFile::getInstance($this, 'avatar'); // if no image was uploaded abort the upload if (empty($image)) { return false; } // generate a unique file name $this->avatar = \Yii::$app->security->generateRandomString() . ".{$image->extension}"; // the uploaded image instance return $image; } /** * Process deletion of image * * @return boolean the status of deletion */ public function deleteImage() { $file = $this->getImageFile(); // check if file exists on server if (empty($file) || !file_exists($file)) { return false; } // 删除缓存的旧头像 $avatarCachePath = \Yii::$app->basePath . \Yii::$app->params['avatarCachePath']; $files = glob("{$avatarCachePath}/*_{$this->user->avatar}"); array_walk($files, function ($file) { unlink($file); }); // check if uploaded file can be deleted on server if (!unlink($file)) { return false; } // if deletion successful, reset your file attributes $this->avatar = null; return true; } } ================================================ FILE: frontend/modules/user/models/Donate.php ================================================ 'create'], [['user_id', 'status', 'created_at', 'updated_at'], 'integer'], [['qr_code'], 'image', 'extensions' => 'jpg, png, gif, jpeg', 'maxSize' => 1024 * 1024 * 2, 'tooBig' => \Yii::t('app', 'File has to be smaller than 2MB')], [['description', 'qr_code'], 'string', 'max' => 50] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'user_id' => Yii::t('app', 'User ID'), 'status' => Yii::t('app', 'Status'), 'description' => Yii::t('app', 'Description'), 'qr_code' => Yii::t('app', 'Qr Code'), 'created_at' => Yii::t('app', 'Created At'), 'updated_at' => Yii::t('app', 'Updated At'), ]; } /** * Process upload of image * * @return mixed the uploaded image instance */ public function uploadImage() { // get the uploaded file instance. for multiple file uploads // the following data will return an array (you may need to use // getInstances method) $image = UploadedFile::getInstance($this, 'qr_code'); // if no image was uploaded abort the upload if (empty($image)) { return false; } // generate a unique file name $this->qr_code = \Yii::$app->security->generateRandomString() . ".{$image->extension}"; // the uploaded image instance return $image; } /** * Process deletion of image * * @return boolean the status of deletion */ public function deleteImage() { if (!isset($this->oldAttributes['qr_code']) || !$this->oldAttributes['qr_code']) { return false; } $file = \Yii::$app->basePath . \Yii::$app->params['qrCodePath'] . $this->oldAttributes['qr_code']; // check if file exists on server if (empty($file) || !file_exists($file)) { return false; } // check if uploaded file can be deleted on server if (!unlink($file)) { return false; } return true; } public static function getStatuses() { return [ 1 => '开启', 0 => '停用', ]; } } ================================================ FILE: frontend/modules/user/models/UserAccount.php ================================================ 100], [['client_id'], 'string', 'max' => 255] ]; } public function getIsConnected() { return $this->user_id != null; } /** * @return User */ public function getUser() { return $this->hasOne(User::className(), ['id' => 'user_id']); } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'user_id' => '用户ID', 'provider' => '授权提供商', 'client_id' => 'Client ID', 'data' => 'Data', 'created_at' => '创建时间', ]; } } ================================================ FILE: frontend/modules/user/models/UserMeta.php ================================================ [ 'class' => 'yii\behaviors\TimestampBehavior', 'attributes' => [ ActiveRecord::EVENT_BEFORE_INSERT => 'created_at', ], ], ]; } /** * @inheritdoc */ public function rules() { return [ [['user_id'], 'required'], [['user_id', 'target_id', 'created_at'], 'integer'], [['type', 'target_type'], 'string', 'max' => 100], [['value'], 'string', 'max' => 255] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'user_id' => '用户ID', 'type' => '操作类型', 'value' => '操作类型值', 'target_id' => '目标id', 'target_type' => '目标类型', 'created_at' => '创建时间', ]; } public static function deleteOne($conditions) { $model = self::findOne($conditions); if ($model) { return $model->delete(); } return false; } /** * 判断指定分类下操作是否存在 * @param integer $type 话题还是回复 * @param string $do 动作 * @param integer $targetId 话题ID或者回复ID * @return int|string */ public function isUserAction($type = 0, $do = '', $targetId) { return $this->find()->where([ 'target_id' => $targetId, 'user_id' => Yii::$app->user->id, 'target_type' => $type, 'type' => $do, ])->count(); } /** * 添加新的动作 * @param $type * @param $targetId * @param $do * @return bool */ public function saveNewMeta($type, $targetId, $do) { $data = [ 'target_id' => $targetId, 'user_id' => Yii::$app->user->id, 'target_type' => $type, 'type' => $do, ]; $this->deleteAll($data); $model = $this->find()->where($data)->one(); $this->setAttributes($data); if (!$model) { if ($this->save()) { return true; } else { return array_values($this->getFirstErrors())[0]; } } } public function getTopic() { return $this->hasOne(Topic::className(), ['id' => 'target_id']); } public function getTweet() { return $this->hasOne(Tweet::className(), ['id' => 'target_id']); } public function getComment() { return $this->hasOne(PostComment::className(), ['id' => 'target_id']); } public function beforeSave($insert) { if ($insert) { $userActionNotify = (new NotificationService)->findUserActionNotify($this); if ($userActionNotify) { $userActionNotify->delete(); } // 点赞、感谢和收藏会收到通知 if (in_array($this->type, ['like', 'favorite', 'thanks'])) { switch ($this->target_type) { case 'topic': (new NotificationService)->newActionNotify( $this->target_type . '_' . $this->type, Yii::$app->user->id, $this->topic->user_id, $this->topic ); break; case 'tweet': (new NotificationService)->newActionNotify( $this->target_type . '_' . $this->type, Yii::$app->user->id, $this->tweet->user_id, $this->tweet ); break; case 'comment': (new NotificationService)->newActionNotify( $this->target_type . '_' . $this->type, Yii::$app->user->id, $this->comment->user_id, $this->comment->topic, $this->comment ); break; default: break; } } } return parent::beforeSave($insert); } public function beforeDelete() { if (parent::beforeDelete()) { $userActionNotify = (new NotificationService)->findUserActionNotify($this); if ($userActionNotify) { $userActionNotify->status = 0; $userActionNotify->save(); } return true; } else { return false; } } } ================================================ FILE: frontend/modules/user/views/default/_view.php ================================================ context->action->id) { case 'show': // 回复 if ($model->post) { echo Html::a( Html::encode($model->post->title), ["/{$model->post->type}/default/view", 'id' => $model->post->id], ['class' => 'list-group-item-heading'] ); echo Html::tag('span', Yii::$app->formatter->asRelativeTime($model->created_at), ['class' => 'ml5 fade-info']); echo Html::tag('div', HtmlPurifier::process(Markdown::process($model->comment, 'gfm')), ['class' => 'markdown-reply']); } break; case 'favorite': case 'like': // 收藏 echo Html::tag('i', '', ['class' => 'fa fa-bookmark red mr5']); echo Html::a( Html::encode($model->topic->title), ["/{$model->topic->type}/default/view", 'id' => $model->topic->id], ['class' => 'list-group-item-heading'] ); echo Html::tag('span', Yii::$app->formatter->asRelativeTime($model->topic->created_at), ['class' => 'ml5 fade-info']); echo Html::beginTag('p', ['class' => 'list-group-item-text title-info']); echo Html::a($model->topic->category->name, ["/{$model->topic->type}/default/index", 'node' => $model->topic->category->alias]); echo ' • '; echo Html::beginTag('span'); echo "{$model->topic->like_count} 个赞 • {$model->topic->comment_count} 条回复"; echo Html::endTag('span'); echo Html::endTag('p'); break; case 'point': // 积分 echo Html::tag('i', '', ['class' => 'fa fa-money red mr5']); echo Html::encode($model->description); echo Html::tag('span', Yii::$app->formatter->asRelativeTime($model->created_at), ['class' => 'ml5 fade-info']); break; default: // post 文章 echo Html::a( Html::encode($model->title), ["/{$model->type}/default/view", 'id' => $model->id], ['class' => 'list-group-item-heading'] ); echo Html::tag('span', Yii::$app->formatter->asRelativeTime($model->created_at), ['class' => 'ml5 fade-info']); echo Html::beginTag('p', ['class' => 'list-group-item-text title-info']); echo Html::a($model->category->name, ["/{$model->type}/default/index", 'node' => $model->category->alias]); echo ' • '; echo Html::beginTag('span'); echo "{$model->like_count} 个赞 • {$model->comment_count} 条回复"; echo Html::endTag('span'); echo Html::endTag('p'); break; } ?> ================================================ FILE: frontend/modules/user/views/default/show.php ================================================ title = Html::encode($user->username); // $this->params['breadcrumbs'][] = $this->title; $username = Yii::$app->getRequest()->getQueryParam('username'); /** @var User $user*/ ?>
getUserAvatar(100), ['class' => 'media-object']);?>

username)) ?>

id ?> 位会员

role)['name']?>
个人信息
  • 加入于 formatter->asDateTime($user->userInfo->created_at) ?>
  • userInfo->location): ?>
  • 城市 userInfo->location) ?>
  • userInfo->company): ?>
  • 公司 userInfo->company) ?>
  • userInfo->github): ?>
  • GitHub userInfo->github), Html::encode($user->userInfo->github)) ?>
  • 最后登录时间 formatter->asRelativeTime($user->userInfo->last_login_time) ?>
  • tagline): ?>
  • 签名 tagline) ?>
userInfo->info): ?>
个人简介
userInfo->info) ?>
userInfo->website): ?>
个人网站
userInfo->website)) { $user->userInfo->website ='http://' . $user->userInfo->website; } echo Html::a(Html::encode($user->userInfo->website), Html::encode($user->userInfo->website)) ?>
个人成就
  • 发表文章次数 userInfo->post_count ?>
  • 发布回复次数 userInfo->comment_count ?>
  • 个人主页浏览次数 userInfo->view_count ?>
$dataProvider, 'itemOptions' => ['class' => 'list-group-item'], 'summary' => false, 'itemView' => '_view', 'options' => ['class' => 'list-group'], ]) ?>
================================================ FILE: frontend/modules/user/views/setting/_alert.php ================================================ enableFlashMessages): ?>
session->getAllFlashes() as $type => $message): ?>
================================================ FILE: frontend/modules/user/views/setting/_menu.php ================================================ user->identity; $networksVisible = count(Yii::$app->authClientCollection->clients) > 0; ?>

getUserAvatar(24), ['class' => 'img-rounded', 'alt' => $user->username]);?> username ?>

[ 'class' => 'nav nav-pills nav-stacked' ], 'items' => [ ['label' => '个人资料', 'url' => ['/user/setting/profile']], ['label' => '账号设置', 'url' => ['/user/setting/account']], ['label' => '更换头像', 'url' => ['/user/setting/avatar']], ['label' => '打赏设置', 'url' => ['/user/setting/donate']], ['label' => '账号绑定', 'url' => ['/user/setting/networks'], 'visible' => $networksVisible], ] ]) ?>
================================================ FILE: frontend/modules/user/views/setting/account.php ================================================ title = '账号设置'; // $this->params['breadcrumbs'][] = $this->title; ?> render('_alert', ['module' => Yii::$app->getModule('user')]) ?>
render('_menu') ?>
title) ?>
'account-form', 'options' => ['class' => 'form-horizontal'], 'fieldConfig' => [ 'template' => "{label}\n
{input}
\n
{error}\n{hint}
", 'labelOptions' => ['class' => 'col-lg-3 control-label'], ], 'enableAjaxValidation' => true, 'enableClientValidation' => false, ]); ?> field($model, 'email') ?> field($model, 'username') ?> field($model, 'tagline') ?> field($model, 'new_password')->passwordInput()->hint('不填写则不修改密码') ?>
field($model, 'current_password')->passwordInput() ?>
'btn btn-success']) ?>
================================================ FILE: frontend/modules/user/views/setting/avatar.php ================================================ title = Yii::t('app', 'Avatar'); ?>
render('_menu') ?>
title) ?>
'account-form', 'options' => ['enctype' => 'multipart/form-data'], ]); ?> user->getUserAvatar(200)); ?> user->getUserAvatar(50)); ?> user->getUserAvatar(24)); ?>

field($model, 'avatar')->fileInput(); ?>
'btn btn-success']) ?>
================================================ FILE: frontend/modules/user/views/setting/donate.php ================================================ title = Yii::t('app', 'Donate'); /** @var \frontend\modules\user\models\Donate $model */ ?>
render('_menu') ?>
title) ?>
['class' => 'form-horizontal', 'enctype' => 'multipart/form-data'], 'fieldConfig' => [ 'template' => "{label}\n
{input}
\n
{error}\n{hint}
", 'labelOptions' => ['class' => 'col-lg-3 control-label'], ], ]); ?> qr_code): ?>
params['qrCodeUrl'] . $model->qr_code, ['class' => 'img']) ?>
field($model, 'qr_code')->fileInput(); ?>
field($model, 'status')->dropDownList(\frontend\modules\user\models\Donate::getStatuses()) ?> field($model, 'description')->textarea(['rows' => 2]) ?>
'btn btn-success']) ?>
================================================ FILE: frontend/modules/user/views/setting/networks.php ================================================ title = 'Networks'; // $this->params['breadcrumbs'][] = $this->title; ?> render('_alert', ['module' => Yii::$app->getModule('user')]) ?>
render('_menu') ?>
title) ?>
['/user/setting/connect'], 'accounts' => $user->accounts, 'autoRender' => false, 'popupMode' => false ]) ?> getClients() as $client): ?>
'auth-icon ' . $client->getName()]) ?> getTitle() ?> isConnected($client) ? Html::a('Disconnect', $auth->createClientUrl($client), [ 'class' => 'btn btn-danger btn-block', 'data-method' => 'post', ]) : Html::a('Connect', $auth->createClientUrl($client), [ 'class' => 'btn btn-success btn-block' ]) ?>
================================================ FILE: frontend/modules/user/views/setting/profile.php ================================================ title = '个人资料'; // $this->params['breadcrumbs'][] = $this->title; ?>
render('_menu') ?>
title) ?>
'profile-form', 'options' => ['class' => 'form-horizontal'], 'fieldConfig' => [ 'template' => "{label}\n
{input}
\n
{error}\n{hint}
", 'labelOptions' => ['class' => 'col-lg-3 control-label'], ], 'enableAjaxValidation' => true, 'enableClientValidation' => false, 'validateOnBlur' => false, ]); ?> field($model, 'location') ?> field($model, 'company') ?> field($model, 'website') ?> field($model, 'github') ?> field($model, 'info')->textarea(['rows' => 6]) ?>
'btn btn-success']) ?>
================================================ FILE: frontend/runtime/.gitignore ================================================ * !.gitignore ================================================ FILE: frontend/views/layouts/main.php ================================================ beginPage() ?> <?= Html::encode($this->title) ?> - <?= \Yii::$app->setting->get('siteTitle') ?> head() ?> beginBody() ?>
isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], ]) ?>
setting->get('siteAnalytics'); ?>
registerJs( // 'Config = {emojiBaseUrl: "' . $emojify->baseUrl . '"};', // \yii\web\View::POS_HEAD //); ?> endBody() ?> endPage() ?> ================================================ FILE: frontend/views/notification/_item.php ================================================ * createTime : 2015/4/23 14:52 * description: */ use yii\helpers\Html; use yii\helpers\HtmlPurifier; ?> status && $model->post): ?>
fromUser->userAvatar, ['class' => 'media-object img-circle']), ['/user/default/show', 'username' => $model->fromUser['username']] ); ?>
fromUser['username'], ['/user/default/show', 'username' => $model->fromUser['username']])); ?> getlable($model->type) ?> post->title), ['/topic/default/view', 'id' => $model->post_id], ['title' => $model->post->title]); ?> formatter->asRelativeTime($model->created_at), ['title' => Yii::$app->formatter->asDatetime($model->created_at)]) ?> 'new label label-warning']); } ?>
data, 'gfm')) ?>
'fa fa-trash']), ['/notification/delete', 'id' => $model->id], [ 'data' => [ 'method' => 'post', ], ] ) ?>
================================================ FILE: frontend/views/notification/index.php ================================================ title = Yii::t('app', 'Notifications'); ?>
'fa fa-trash']) . ' 清空', '/notification/clear', [ 'data' => [ 'disable-with' => "清空中...", 'method' => 'post', ], 'class' => 'btn btn-danger' ] ) ?> title, ['class' => 'pull-right']) ?>
$dataProvider, 'itemOptions' => ['class' => 'media notification'], 'summary' => false, 'itemView' => '_item', 'options' => ['class' => 'panel-body'], 'viewParams' => ['notifyCount' => $notifyCount] ]) ?>
================================================ FILE: frontend/views/partials/markdwon_help.php ================================================ * createTime : 15/4/12 上午10:38 * description: Markdown 提示 */ ?> ================================================ FILE: frontend/views/partials/users.php ================================================ * createTime : 2016/4/19 14:44 * description: */ use yii\helpers\Html; /** @var \yii\base\Object $model */ /** @var \common\models\User $value */ ?> $value): ?>
userAvatar, ['class' => 'media-object']), ['/user/default/show', 'username' => $value['username']], ['title' => $value['username']] ); ?>
$value['username']], ['title' => $value['username']] ); ?>
积分:merit ? $value->merit->merit : 0 ?>
================================================ FILE: frontend/views/site/_item.php ================================================
  • 'badge badge-reply-count']), ['/topic/default/view', 'id' => $model->id, '#' => 'comment' . $model['comment_count']], ['class' => 'pull-right'] ); ?>
    user->userAvatar, ['class' => 'media-object']), ['/user/default/show', 'username' => $model->user['username']] ); ?>
    title, ['/topic/default/view', 'id' => $model->id], ['title' => $model->title] ); ?> status == 2) ? Html::tag('i', '', ['class' => 'fa fa-trophy excellent']) : null ?>
    like_count) { echo Html::a(Html::tag('span', ' ' . $model->like_count . ' ', ['class' => 'fa fa-thumbs-o-up']), ['/topic/default/view', 'id' => $model->id], ['class' => 'remove-padding-left'] ), '•'; } echo Html::a( $model->category->name, ['/topic/default/index', 'node' => $model->category->alias], ['class' => 'node'] ), '•', Html::a( $model->user['username'], ['/user/default/show', 'username' => $model->user['username']] ), '•', Html::tag('span', Yii::$app->formatter->asRelativeTime($model->created_at)); ?>
  • ================================================ FILE: frontend/views/site/about.php ================================================ title = '关于'; $content = ' #### 这里是 Yii 中文社区 - 爱 PHP,爱 Yii - 爱互联网,爱 Web 开发,爱最新最潮的技术 - 爱学习,爱沟通,也爱传播 - 我们不管你是谁,只要你喜欢 PHP,喜欢 Yii - 这里是 PHP & Yii 的中国社区,作我们最好的交流和沟通的大本营 一直以来,Yii 在中国都没有一个靠谱的社区,我们几个打算认真的把这个站做起来,改善中国 Yiier 交流的方式。我们是一个非营利组织,它旨在为中国的 PHP 和 Yii 爱好者提供一个自由,开放的交流平台。 enjoy coding! enjoy yii! #### 最后 - 感谢 [Ruby-China](https://github.com/ruby-china/ruby-china) 的开源代码。 - 感谢 [PHPHub](https://github.com/summerblue/phphub) 的开源代码。 - 感谢 [huajuan](https://github.com/callmez/huajuan) 的开源代码。 - 最后再感谢一下女朋友的支持 <(▰˘◡˘▰)>。 '; ?>
    关于
    ================================================ FILE: frontend/views/site/contact.php ================================================ title = '联系我们'; $content = ' ## QQ群 - Yii2 中国交流群:343188481 - Get√Yii 核心开发者群:321493381(本群只接受参与本站开发的 Yiier) ## 个人联系 - QQ:314494687 - Mail:caizhenghai[#]gmail.com '; ?>
    title ?>
    ================================================ FILE: frontend/views/site/contributors.php ================================================ title = '贡献者'; $content = ' 以下是本社区的贡献者名单,排名不分先后。 #### 赞助者 - XXX. #### 社区维护 - [forecho](/member/forecho) - [zghack](/member/zghack) #### 网站功能开发者 [https://github.com/iiyii/getyii/contributors](https://github.com/iiyii/getyii/contributors) #### Logo 设计 - [forecho](/member/forecho) #### 如何贡献? 有钱出钱,有力出力 Github 项目地址: [https://github.com/iiyii/getyii](https://github.com/iiyii/getyii) Fork 以后提交你的改进,我们会根据情况合并到主线中去,并将你列入贡献者名单。 #### 如何赞助? ![加我微信](/images/wechat-pay.png) ![加我支付宝](/images/ali-pay.png) '; ?>
    title) ?>
    ================================================ FILE: frontend/views/site/error.php ================================================ title = $name; ?>

    title) ?>

    The above error occurred while the Web server was processing your request.

    Please contact us if you think this is a server error. Thank you.

    ================================================ FILE: frontend/views/site/getstart.php ================================================ title = 'Yii 新手入门'; // $this->params['breadcrumbs'][] = $this->title; $content = ' ## Yii 新手入门 ### Yii 入门 - [Yii Framework 官网](http://www.yiiframework.com/) - [Yii2-中文化组织](https://github.com/yii2-chinesization) - [Composer 中文文档](https://github.com/5-say/composer-doc-cn) #### Yii1 - [Yii1 权威指南中文版](http://www.yiiframework.com/doc/guide/1.1/zh_cn/index) - [Yii1 Api 英文版](http://www.yiiframework.com/doc/api/) - [Yii1 Demos](http://www.eha.ee/labs/yiiplay/index.php/et) - [Yii1 Demos](http://demo.bsourcecode.com/yiiframework/) - [Yii1-extension Demo](http://www.eha.ee/labs/yiiplay/index.php/et) #### Yii2 - [Yii2 权威指南英文版](http://www.yiiframework.com/doc-2.0/guide-index.html) - [Yii2 Api 英文版](http://www.yiiframework.com/doc-2.0/index.html) - [Yii 2.0权威指南中文版](http://yii2.techbrood.com/guide-index.html) - [Yii 2.0权威指南中文版](http://yii2.yiibar.com/guide-zh-CN/guide-README.html) - [Yii 2.0权威指南中文版](http://www.yiifans.com/yii2/guide/index.html) - [深入理解Yii2.0](http://www.digpage.com) - [Krajee Yii Extensions](http://demos.krajee.com/) ### Yii 资源 - [Yii 基础教程博客](http://www.bsourcecode.com/) - [CSDN Yii 开发教程](http://blog.csdn.net/column/details/mapdigityiiframework.html) ### Yii 书籍 - [Yii框架图书](http://www.yiibook.com/) ### Yii 优秀开源 #### 基于Yii1 - [yupe](https://github.com/yupe/yupe) - [CiiMS](https://github.com/charlesportwoodii/CiiMS) - [Yincart](https://github.com/yincart/basic) - [dlfblog](https://github.com/windsdeng/dlfblog) - [FirCMS](https://github.com/poctsy/fircms) - [birdbbs](https://github.com/outman/birdbbs) - [dcms](https://github.com/djfly/dcms) - [bagecms](http://www.bagecms.com/) #### 基于Yii2 - [GetYii](https://github.com/iiYii/getyii) - [huajuan](https://github.com/callmez/huajuan) - [dcms2](https://github.com/djfly/dcms2) - [yii2-adminlte](https://github.com/funson86/yii2-adminlte) - [yii2-simple](https://github.com/azraf/yii2-simple) - [dotplant2](https://github.com/DevGroup-ru/dotplant2) ### 贡献者 - [forecho](/member/forecho) ### 最后 欢迎大家跟我联系提供更多资料。 '; ?>
    Wiki 列表
    ================================================ FILE: frontend/views/site/index.php ================================================ title = \Yii::$app->setting->get('siteName'); /** @var array $headline */ /** @var array $topics */ /** @var array $statistics */ /** @var array $users */ /** @var \yii\web\View $this */ ?>

     

    $vlaue) { echo $this->render('_item', ['model' => $vlaue]); } } else { echo \Yii::t('app', 'Dont have any data Yet'); } ?>

    社区会员榜

    render('/partials/users', ['model' => $users]); ?>
    ================================================ FILE: frontend/views/site/login.php ================================================ title = Yii::t('app', 'Login'); ?>
    title) ?>
    'login-form']); ?> field($model, 'username', ['inputOptions' => ['tabindex' => '1']]) ?> field($model, 'password', ['inputOptions' => ['class' => 'form-control', 'tabindex' => '2']])->passwordInput()->label('密码' . ' (' . Html::a('忘记密码?', ['site/request-password-reset'], ['tabindex' => '5']) . ')') ?> field($model, 'rememberMe')->checkbox() ?>
    title, ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
    用其他平台的帐号登录

    ['/user/security/auth'] ]) ?>
    ================================================ FILE: frontend/views/site/markdown.php ================================================ title = 'Markdown 教程'; // $this->params['breadcrumbs'][] = $this->title; $content = ' # Guide 这是一篇讲解如何正确使用 **Markdown** 的排版示例,学会这个很有必要,能让你的文章有更佳清晰的排版。 > 引用文本:Markdown is a text formatting syntax inspired ## 排版 请注意单词拼写,以及中英文排版,https://github.com/sparanoid/chinese-copywriting-guidelines ## 语法指导 ### 普通内容 这段内容展示了在内容里面一些小的格式,比如: - **加粗** - `**加粗**` - *倾斜* - `*倾斜*` - ~~删除线~~ - `~~删除线~~` - `Code 标记` - ``Code 标记`` - [超级链接](http://github.com) - `[超级链接](http://github.com)` - [caizhenghai@gmail.com](mailto:caizhenghai@gmail.com) - `[caizhenghai@gmail.com](mailto:caizhenghai@gmail.com)` - ![图片](http://7xjanb.com1.z0.glb.clouddn.com/logo.png) - `![图片](http://7xjanb.com1.z0.glb.clouddn.com/logo.png)` 注:暂不支持上传图片,请使用外链图片。推荐图床:http://drp.io/ 和 https://imgur.com/ ### 提及用户 @forecho @caicai ... 通过 @ 可以在发帖和回帖里面提及用户,信息提交以后,被提及的用户将会收到系统通知。以便让他来关注这个帖子或回帖。切记:@某人之后有一个空格。 ### 表情符号 Emoji 支持表情符号,你可以用系统默认的 Emoji 符号(无法支持 Chrome 以及 Windows 用户)。 也可以用图片的表情。 #### 一些表情例子 :smile: :laughing: :dizzy_face: :sob: :cold_sweat: :sweat_smile: :cry: :triumph: :heart_eyes: :satisfied: :relaxed: :sunglasses: :weary: :+1: :-1: :100: :clap: :bell: :gift: :question: :bomb: :heart: :coffee: :cyclone: :bow: :kiss: :pray: :shit: :sweat_drops: :exclamation: :anger: 更多表情请访问:[http://www.emoji-cheat-sheet.com](http://www.emoji-cheat-sheet.com) ### 大标题 - Heading 3 你可以选择使用 H2 至 H6,使用 ##(N) 打头,H1 不能使用,会自动转换成 H2。 > NOTE: 别忘了 # 后面需要有空格! #### Heading 4 ##### Heading 5 ###### Heading 6 ### 代码块 #### 普通 ``` *emphasize* **strong** _emphasize_ __strong__ @a = 1 ``` #### 语法高亮支持 如果在 ``` 后面更随语言名称,可以有语法高亮的效果哦,比如: ##### 演示 PHP 代码高亮 ```php public function getDataCellValue($model, $key, $index) { $value = parent::getDataCellValue($model, $key, $index); return ArrayHelper::getValue($this->enum, $value, $value); } ``` > Tip: 语言名称支持下面这些: `ruby`, `python`, `js`, `html`, `php`, `css`, `coffee`, `bash`, `json`, `xml` ... ### 有序、无序列表 #### 无序列表 - PHP - Yii - ActiveRecord - Go - Gofmt - Revel - Node.js - Koa - Express #### 有序列表 1. Node.js 1. Express 2. Koa 3. Sails 2. PHP 1. Yii 2. Laravel 3. Go ### 段落 留空白的换行,将会被自动转换成一个段落,会有一定的段落间距,便于阅读。 请注意后面 Markdown 源代码的换行留空情况。 '; ?>
    title; ?>
    ================================================ FILE: frontend/views/site/requestPasswordResetToken.php ================================================ title = '找回密码'; ?>
    title) ?>
    'request-password-reset-form']); ?> field($model, 'email') ?>
    'btn btn-primary']) ?>
    ================================================ FILE: frontend/views/site/resetPassword.php ================================================ title = '密码重置'; ?>
    title) ?>
    'reset-password-form']); ?> field($model, 'password')->passwordInput() ?>
    'btn btn-primary']) ?>
    ================================================ FILE: frontend/views/site/signup.php ================================================ title = Yii::t('app', 'Sign up'); ?>
    title) ?>
    'form-signup', 'enableAjaxValidation' => true, 'enableClientValidation' => false ]); ?> field($model, 'username') ?> field($model, 'email') ?> field($model, 'password')->passwordInput() ?>
    'btn btn-primary', 'name' => 'signup-button']) ?>
    ================================================ FILE: frontend/views/site/tags.php ================================================ title = '标签云'; ?>
    title; ?>
    count / 3); echo Html::a($tag->name, ['/topic/default/index', 'tag' => $tag->name], ['class' => 'cloud-' . $i]); } ?>
    ================================================ FILE: frontend/views/site/timeline.php ================================================ title = '时间线'; $content = ' **2015年5月1日** - 网站重新上线,全新改版只做社区! - 原来的版本命名为[V1](https://github.com/iiyii/getyii/tree/v1),可能不会再更新,没有通知系统。 **2015年4月15日** - 解决几个BUG **2015年4月12日** - 更换 MarkDown 在线编辑器。 - 添加统计功能 **2015年3月2日** - 在 [yiichina](http://www.yiichina.com/topic/5685) 上发帖推广 **2015年2月14日** - 吸引到第一位开发者-[kevin](http://www.getyii.com/member/kevin) 注册账号 **2015年2月06日** - 上线测试 '; ?>
    title ?>
    ================================================ FILE: frontend/views/site/users.php ================================================ title = '活跃用户'; ?>
    TOP 100 活跃会员
    目前已经有 位会员加入了 Get Yii
    render('/partials/users', ['model' => $model]); ?>
    ================================================ FILE: frontend/web/.gitignore ================================================ /index.php /index-test.php ================================================ FILE: frontend/web/.htaccess ================================================ # use mod_rewrite for pretty URL support RewriteEngine on # If a directory or a file exists, use the request directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # Otherwise forward the request to index.php RewriteRule . index.php # 七牛云存储的回调会传送HTTP_AUTHORIZATION认证秘钥,一定要放在最rewrite的后面.防止影响 # PHP在CGI模式下的认证信息的获取 # RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] ================================================ FILE: frontend/web/assets/.gitignore ================================================ * !.gitignore ================================================ FILE: frontend/web/css/global.css ================================================ /*屏蔽流氓宽带商强行插入的广告*/ body > iframe { opacity: 0; display: none; } body { font-family: "Helvetica Neue", PingFang SC, Lantinghei SC, Microsoft Yahei, Arial, "Hiragino Sans GB", "Hiragino Sans GB W3", "Microsoft YaHei", "Wenquanyi Micro Hei", sans-serif; } .text-center { text-align: center; } .fl { float: left; } .fr { float: right; } .pl0 { padding-left: 0 } .pr0 { padding-right: 0 } .p0 { padding: 0 } .mp0 p, .m0 { margin: 0; } .mt10 { margin-top: 10px; } .mt0 { margin-top: 0; } .mt5 { margin-top: 5px; } .mb0 { margin-bottom: 0; } .mt15 { margin-top: 15px; } .mb20 { margin-bottom: 20px; } .ml5 { margin-left: 5px; } .mr5 { margin-right: 5px; } .mr15 { margin-right: 15px; } h1 { font-size: 24px; } abbr[title] { border-bottom: 0; } .divider { border-bottom: 1px solid #ddd; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-weight: bold; } h2 { font-size: 22px; } h3 { font-size: 20px; } h4 { font-size: 18px; } .fs12 { font-size: 12px; } .br0 { border-radius: 0; } a { color: #555; } a:hover, a:focus { color: #4d5256; } ================================================ FILE: frontend/web/css/site-ruyi.css ================================================ html, body { height: 100%; background-color: #fff; font-family: 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, tahoma, arial, simsun, 宋体; } .wrap { min-height: 100%; height: auto; margin: 0 auto -60px; padding: 0 0 60px; } .footer { background-color: #fff; border-top: 1px solid #ddd; padding-top: 50px; padding-bottom: 30px; } .jumbotron { text-align: center; background-color: transparent; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } .not-set { color: #c55; font-style: italic; } /* add sorting icons to gridview sort links */ a.asc:after, a.desc:after { position: relative; top: 1px; display: inline-block; font-style: normal; font-weight: normal; line-height: 1; padding-left: 5px; } a.asc:after { content: /*"\e113"*/ "\e151"; } a.desc:after { content: /*"\e114"*/ "\e152"; } .sort-numerical a.asc:after { content: "\e153"; } .sort-numerical a.desc:after { content: "\e154"; } .sort-ordinal a.asc:after { content: "\e155"; } .sort-ordinal a.desc:after { content: "\e156"; } .grid-view th { white-space: nowrap; } .hint-block { display: block; margin-top: 5px; color: #999; } .error-summary { color: #a94442; background: #fdf7f7; border-left: 3px solid #eed3d7; padding: 10px 20px; margin: 0 0 15px 0; } /* 自定义 */ .navbar, .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background: #FCFCFC; border-bottom: 1px solid #E5E5E5; } .navbar a, .navbar-default .navbar-nav > li > a { color: #000; } .navbar .nav li.active a:focus, .navbar .nav li.active a:hover, .nav > li > a:hover, .nav > li > a:focus { background-color: #F1F1F1; color: #000; } .navbar .nav li.active a { background-color: #F0F0F0; color: #000; } .navbar-toggle .icon-bar { background-color: #5CB85C; } @media (min-width: 1400px) { .container { width: 1400px; } } .list-group { margin-bottom: 0; background-color: #f5f5f5; } .col-md-10 { padding-left: 0; } .site-index-topic .list-group-item:last-child { border-bottom: 0px; } .topic-view .info { color: #c0c0c0; font-size: 12px; } .topic-view .panel-heading { padding: 15px; } .topic-view .panel-heading h1 { font-size: 24px; } .list-group-item { border: none; margin-bottom: 0; border-bottom: 1px solid #ddd; } .pagination { margin-left: 15px; } .article a { color: #008E59; } .topic-view .active .fa, .filter .active, .red { color: #EB5424; } #reply-notice { color: gray; padding-top: 16px; margin-bottom: 16px; border: 1px dashed; } .opts a:link, .opts a:visited { color: #666; text-decoration: none; } .opts a { margin-right: 5px; } #topic-view .info .opts a { margin-left: 5px; color: #999; } .empty { padding: 10px 15px; } #md-preview { padding: 10px; margin-bottom: 15px; border: 1px dashed #ccc; } /*代码高亮*/ .article pre, .markdown-reply pre { padding: 0; } .deleted { text-decoration: line-through; color: #e1e1e1; } #about-us h5 { font-size: 13px; } .notification-count .new { color: #F86334; } .notification-count span { margin-left: 3px; font-size: 12px; } .title-info { color: #aaa; font-size: 12px; margin-top: 10px; } .title-info .node { padding: 1px 5px; text-decoration: none; background-color: #efefef; } .title-info a { color: #666; } .notification-index .notification { position: relative; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #F0F4F6; } .badge-reply-count { margin-top: 15px; background-color: #98ACDF; } a:visited .badge-reply-count { background-color: #d7d7d7; } .excellent { color: #EB5424; } .node-box dt { color: #999; font-weight: 400; } .node-box dd, .node-box dt { padding: 15px 0 4px; } .ribbon-excellent { font-size: 13px; background: #FCF8F7; padding: 3px 15px; border-top: 1px solid #eae5e4; color: #aAa5a4; margin: 0 -15px -15px; } .side-bar .list { padding-left: 15px; } .tag-cloud a { padding: 10px; } .tag-cloud .cloud-1 { font-size:120%; } .tag-cloud .cloud-2 { font-size:140%; } .tag-cloud .cloud-3 { font-size:160%; } .tag-cloud .cloud-4 { font-size:180%; } .tag-cloud .cloud-5 { font-size:200%; } .tag-cloud .cloud-6 { font-size:220%; } /* add by ruzuojun */ #floatButton {position:fixed;top:50%;right:0;z-index:9999999;} #floatButton .btn:focus{outline:0} @media screen and (max-width: 767px) { #floatButton {display:none;} } #pageQrcode img.qrcode{position:absolute;right:50px;bottom:-34px;padding:10px;background:#fff;border:1px solid #ccc;border-radius:4px;display:none} #pageQrcode:hover img.qrcode{display:block} ================================================ FILE: frontend/web/css/site.css ================================================ html, body { height: 100%; background-color: #f0f0f0; padding-top: 35px; } .wrap { min-height: 100%; height: auto; margin: 0 auto -60px; padding: 0 0 60px; } .footer { background-color: #fff; border-top: 1px solid #ddd; padding-top: 50px; padding-bottom: 30px; } .jumbotron { text-align: center; background-color: transparent; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } .not-set { color: #c55; font-style: italic; } /* add sorting icons to gridview sort links */ a.asc:after, a.desc:after { position: relative; top: 1px; display: inline-block; font-style: normal; font-weight: normal; line-height: 1; padding-left: 5px; } a.asc:after { content: /*"\e113"*/ "\e151"; } a.desc:after { content: /*"\e114"*/ "\e152"; } .sort-numerical a.asc:after { content: "\e153"; } .sort-numerical a.desc:after { content: "\e154"; } .sort-ordinal a.asc:after { content: "\e155"; } .sort-ordinal a.desc:after { content: "\e156"; } .grid-view th { white-space: nowrap; } .hint-block { display: block; margin-top: 5px; color: #999; } .error-summary { color: #a94442; background: #fdf7f7; border-left: 3px solid #eed3d7; padding: 10px 20px; margin: 0 0 15px 0; } /* 自定义 */ .navbar, .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background: #FFF; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.11); } .navbar a, .navbar-default .navbar-nav > li > a { color: #333; } .navbar .nav li.active a:focus, .navbar .nav li.active a:hover, .nav > li > a:hover, .nav > li > a:focus { background-color: #FFF; color: #000; } .navbar .nav li.active a { background-color: #FFF; border-bottom: 3px solid #5CB85C; color: #5CB85C; } .navbar-toggle .icon-bar { background-color: #5CB85C; } .list-group { margin-bottom: 0; background-color: #f5f5f5; } .site-index-topic .list-group-item:last-child { border-bottom: 0; } .topic .tab { background-color: #fff; } .topic .tab a { padding: 5px 8px 5px 8px; border-radius: 3px; } .topic .tab a:hover { background-color: #f5f5f5; color: #000; text-decoration: none; } .topic .tab a.active { background-color: #5CB85C; color: #fff; } .topic .children a.children-node { padding: 0 10px; } .topic .title { font-size: 16px; } .node-header .title { font-size: 24px; color: #333; margin-bottom: 8px; } .node-header .title .total { color: #999; font-size: 14px; margin-left: 10px; } .topic-view .info { color: #c0c0c0; font-size: 12px; } .topic-view .panel-heading { padding: 15px; } .topic-view .panel-heading h1 { font-size: 24px; } .list-group-item { border: none; margin-bottom: 0; border-bottom: 1px solid #ddd; } .pagination { margin-left: 15px; } .article a { color: #008E59; } .article img, .markdown-reply img, .text-center img, .img { max-width: 100%; } .tweet .active .fa, .topic-view .active .fa, .filter .active, .red { color: #EB5424; } #reply-notice { color: gray; padding-top: 16px; margin-bottom: 16px; border: 1px dashed; } .opts a:link, .opts a:visited { color: #666; text-decoration: none; } .opts a { margin-right: 5px; } #topic-view .info .opts a { margin-left: 5px; color: #999; } .empty { padding: 10px 15px; } #md-preview { padding: 10px; margin-bottom: 15px; border: 1px dashed #ccc; } .article { margin-top: 0; margin-bottom: 0; margin-right: auto; line-height: 2; color: #424b50; word-wrap: break-word; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; counter-reset: entry825h2 entry825h3 entry825h4 entry825h5 entry825h6; } .article p { padding: 0; margin-top: 12px; margin-bottom: 12px; } /*代码高亮*/ .article pre, .user-default-index pre, .markdown pre, .markdown-reply pre, .tweet pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 0; } .article blockquote, .markdown-reply blockquote, #md-preview blockquote, blockquote { background-color: #f7f7f7; font-size: inherit; } .deleted { text-decoration: line-through; color: #e1e1e1; } #about-us h5 { font-size: 13px; } .notification-count .new { color: #F86334; } .notification-count span { margin-left: 3px; font-size: 12px; } .title-info { margin-top: 10px; } .fade-info, .title-info { color: #aaa; font-size: 12px; } .title-info .node { padding: 1px 5px; text-decoration: none; background-color: #efefef; } .title-info a { color: #666; } .notification-index .notification { position: relative; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #F0F4F6; } .badge-reply-count { margin-top: 15px; background-color: #98ACDF; } a:visited .badge-reply-count { background-color: #d7d7d7; } .excellent { color: #EB5424; } .node-box dt { color: #999; font-weight: 400; } .node-box dd, .node-box dt { padding: 15px 0 4px; } .ribbon-excellent { font-size: 13px; background: #FCF8F7; padding: 3px 15px; border-top: 1px solid #eae5e4; color: #aAa5a4; margin: 10px -15px -15px; } .side-bar .list { padding-left: 15px; } .tag-cloud a { padding: 10px; } .tag-cloud .cloud-1 { font-size: 120%; } .tag-cloud .cloud-2 { font-size: 140%; } .tag-cloud .cloud-3 { font-size: 160%; } .tag-cloud .cloud-4 { font-size: 180%; } .tag-cloud .cloud-5 { font-size: 200%; } .tag-cloud .cloud-6 { font-size: 220%; } /* add by ruzuojun */ #floatButton { position: fixed; top: 50%; right: 0; z-index: 9999999; } #floatButton .btn:focus { outline: 0 } @media screen and (max-width: 767px) { #floatButton { display: none; } } #pageQrcode img.qrcode { position: absolute; right: 50px; bottom: -34px; padding: 10px; background: #fff; border: 1px solid #ccc; border-radius: 4px; display: none } #pageQrcode:hover img.qrcode { display: block } .index_count { float: left; font-size: 12px; margin-top: 3px; } #editor { border: 1px solid #ddd; padding: 6px 6px 0 6px; } .select2-container--krajee .select2-results > .select2-results__options { max-height: 500px; } img.emoji { width: 1.5em; height: 1.5em; margin: 0 .1em; vertical-align: -0.1em; display: inline-block; } .donate p { margin-top: 10px; font-size: 12px; color: #666; } .follow-info { border-top: 1px solid #f0f0f0; text-align: center; margin-top: 15px; padding-top: 15px; } .follow-info a { display: block; text-decoration: none; } .follow-info a.text { color: #999; } .follow-info a.counter { font-size: 32px; color: #5CB85C; } .tweet h1, .tweet h2, .tweet h3, .tweet h4 { font-size: 14px; } .user-card { margin-bottom: 15px; } .p-fixed { position: fixed; display: block; width: 292px; z-index: 99; } .hide { display: none; } .show { display: inline; } #donate-qr-code { display: none; } .numbers { float: left; width: 80px; height: 42px; top: 10px; left: 0; text-align: center; } .numbers .like-num, .numbers .read-num { float: left; width: 50%; display: block; } .numbers div.like-num p { color: #5CB85C; } ================================================ FILE: frontend/web/js/At.js ================================================ /** * Created by Administrator on 2016/3/10. */ var emojis = ["plus1", "ok_hand", "joy", "clap", "smile", "smirk", "sleepy", "smiley", "heart", "kiss", "copyright", "coffee"]; var emojisList = $.map(emojis, function (value, i) { return {'id': i, 'name': value}; }); $(".field-md-input textarea").atwho({ at: ':', displayTpl: "
  • ${name}
  • ", insertTpl: ":${name}:", data: emojisList }).atwho({ at: "@", data: "/at-users", //data: ["one", "two", "three"], limit: 6 }); $("[data-at-topic]").atwho({ // 动弹话题 at: "#", data: ['每日打卡', '干货分享', '心情', '小贴士'], limit: 6, suffix: '# ' }); $("[data-at-floor]").atwho({ // 楼层 at: "#", data: Array.apply(null, Array(99)).map(function (_, i) { return (i + 1) + '楼'; }), limit: 6, suffix: ' ' }); ================================================ FILE: frontend/web/js/editor.js ================================================ // function insertString (merged, line) { // var editor = ace.edit("markdown"); // editor.$blockScrolling = Infinity; // var source = editor.getValue(); // var prefixBreak = source ? "\n" : ''; // var srcMerged = prefixBreak + merged; // editor.insert(srcMerged); // editor.gotoLine(editor.getCursorPosition().row + line); // } // // $('.insert-codes a').click(function (e) { // e.preventDefault(); // var language = $(this).data('lang'); // insertString("```" + language + "\n\n```\n", -1); // }); // // var $editor = $('#md-input').dropzone({ // // url: "https://sm.ms/api/upload", // // paramName: 'smfile', // // clickable: true, // // previewsContainer: '#dropzone-previewer', // // headers: { 'Cache-Control': null, 'X-Requested-With': null }, // // success: function(file, response){ // // if (response.code == 'success') { // // var img = response.data.url; // // insertString("![](" + img + ")\n", 1); // // } else { // // alert(response.msg); // // } // // } // // }); // // // $('#topic-upload-image').click(function(e){ // // $editor.click(); // // }) // ================================================ FILE: frontend/web/js/jquery.pin.js ================================================ (function ($) { "use strict"; $.fn.pin = function (options) { var scrollY = 0, elements = [], disabled = false, $window = $(window); options = options || {}; var recalculateLimits = function () { for (var i=0, len=elements.length; i"); } var pad = $.extend({ top: 0, bottom: 0 }, options.padding || {}); $this.data("pin", { pad: pad, from: (options.containerSelector ? containerOffset.top : offset.top) - pad.top, to: containerOffset.top + $container.height() - $this.outerHeight() - pad.bottom, end: containerOffset.top + $container.height(), parentTop: parentOffset.top }); $this.css({width: $this.outerWidth()}); $this.parent().css("height", $this.outerHeight()); } }; var onScroll = function () { if (disabled) { return; } scrollY = $window.scrollTop(); var elmts = []; for (var i=0, len=elements.length; i data.end) { $this.css('position', ''); continue; } if (from < scrollY && to > scrollY) { !($this.css("position") == "fixed") && $this.css({ left: $this.offset().left, top: data.pad.top }).css("position", "fixed"); if (options.activeClass) { $this.addClass(options.activeClass); } } else if (scrollY >= to) { $this.css({ left: "", top: to - data.parentTop + data.pad.top }).css("position", "absolute"); if (options.activeClass) { $this.addClass(options.activeClass); } } else { $this.css({position: "", top: "", left: ""}); if (options.activeClass) { $this.removeClass(options.activeClass); } } } elements = elmts; }; var update = function () { recalculateLimits(); onScroll(); }; this.each(function () { var $this = $(this), data = $(this).data('pin') || {}; if (data && data.update) { return; } elements.push($this); $("img", this).one("load", recalculateLimits); data.update = update; $(this).data('pin', data); }); $window.scroll(onScroll); $window.resize(function () { recalculateLimits(); }); recalculateLimits(); $window.load(update); return this; }; })(jQuery); ================================================ FILE: frontend/web/js/main.js ================================================ jQuery(function ($) { function notificationsCount() { var notification = $(".notification-count"); var originalTitle = document.title; if (notification.length > 0) { function scheduleGetNotification() { $.get(location.origin + "/notification/count", function (data) { var nCount = parseInt(data); if (nCount > 0) { $(".notification-count a span").text(nCount); $(".notification-count a").addClass("new"); document.title = "(" + nCount + ") " + originalTitle; } else { document.title = originalTitle; $(".notification-count a span").text(""); $(".notification-count a").removeClass("new"); } setTimeout(scheduleGetNotification, 15000); }); } setTimeout(scheduleGetNotification, 15000); } } notificationsCount(); // 新窗口打开外链 $('a[href^="http://"], a[href^="https://"]').each(function () { var a = new RegExp("/" + window.location.host + "/"); if (!a.test(this.href)) { $(this).click(function (event) { event.preventDefault(); event.stopPropagation(); window.open(this.href, "_blank"); }); } }); // 加载代码高亮 hljs.initHighlightingOnLoad(); emojify.setConfig({ img_dir: "https://cdn.learnku.com/assets/images/emoji/", }); emojify.run(); function localStorage() { $("#md-input").focus(function (event) { // Topic Title ON Topic Creation View localforage.getItem("topic_title", function (err, value) { if ($(".topic-create #topic-title").val() == "" && !err) { $(".topic-create #topic-title").val(value); } }); $(".topic-create #topic-title").keyup(function () { localforage.setItem("topic_title", $(this).val()); }); // Topic Content ON Topic Creation View localforage.getItem("topic_create_content", function (err, value) { if ($(".topic-create #md-input").val() == "" && !err) { $(".topic-create #md-input").val(value); runPreview(); } }); $(".topic-create #md-input").keyup(function () { localforage.setItem("topic_create_content", $(this).val()); runPreview(); }); // Reply Content ON Topic Detail View localforage.getItem("comment_content", function (err, value) { if ($(".topic-view #md-input").val() == "" && !err) { $(".topic-view #md-input").val(value); runPreview(); } }); $(".topic-view #md-input").keyup(function () { localforage.setItem("comment_content", $(this).val()); runPreview(); }); }); // Clear Local Storage on submit $(".topic-create button[type=submit]").click(function (event) { localforage.removeItem("topic_create_content"); localforage.removeItem("topic_title"); }); $(".topic-view button[type=submit]").click(function (event) { localforage.removeItem("comment_content"); }); } localStorage(); //add by ruzuojun $(document) .on("click", "#goTop", function () { $("html,body").animate({ scrollTop: "0px" }, 800); }) .on("click", "#goBottom", function () { $("html,body").animate({ scrollTop: $(".footer").offset().top }, 800); }) .on("click", "#refresh", function () { location.reload(); }); //打赏显示和隐藏切换 $("#donate-btn").click(function () { $("#donate-qr-code").toggle(); }); // 防止重复提交 $("form").on("submit", function () { var $form = $(this), data = $form.data("yiiActiveForm"); if (data) { // 如果是第一次 submit 并且 客户端验证有效,那么进行正常 submit 流程 if (!$form.data("getyii.submitting") && data.validated) { $form.data("getyii.submitting", true); return true; } else { // 否则阻止提交 return false; } } }); // function called if wwads is blocked function ABDetected() { var adBlockDetected_div = document.createElement("div"); adBlockDetected_div.style.cssText = "position: absolute; top: 0; left: 0; width: 100%; background: #fc6600; color: #fff; z-index: 9999999999; font-size: 14px; text-align: center; line-height: 1.5; font-weight: bold; padding-top: 6px; padding-bottom: 6px;"; adBlockDetected_div.innerHTML = "我们的广告服务商 并不跟踪您的隐私,为了支持本站的长期运营,请将我们的网站 加入广告拦截器的白名单。"; document.getElementsByTagName("body")[0].appendChild(adBlockDetected_div); // add a close button to the right side of the div var adBlockDetected_close = document.createElement("div"); adBlockDetected_close.style.cssText = "position: absolute; top: 0; right: 10px; width: 30px; height: 30px; background: #fc6600; color: #fff; z-index: 9999999999; line-height: 30px; cursor: pointer;"; adBlockDetected_close.innerHTML = "×"; adBlockDetected_div.appendChild(adBlockDetected_close); // add a click event to the close button adBlockDetected_close.onclick = function () { this.parentNode.parentNode.removeChild(this.parentNode); }; } function docReady(t) { "complete" === document.readyState || "interactive" === document.readyState ? setTimeout(t, 1) : document.addEventListener("DOMContentLoaded", t); } //check if wwads' fire function was blocked after document is ready with 3s timeout (waiting the ad loading) docReady(function () { setTimeout(function () { if (window._AdBlockInit === undefined) { ABDetected(); } }, 3000); }); }); ================================================ FILE: frontend/web/js/nav.js ================================================ /** * Created by ruzuojun on 2015/6/26. */ // $(".pinned").pin({containerSelector: ".container", minWidth: 940}); // $(function () { // $('[data-toggle="tooltip"]').tooltip() // }) ================================================ FILE: frontend/web/js/topic.js ================================================ jQuery(function ($) { //赞, 踩, 收藏 等操作 $(document).on('click', '[data-do]', function (e) { var _this = $(this), _id = _this.data('id'), _do = _this.data('do'), _type = _this.data('type'); if (_this.is('a')) e.preventDefault(); $.ajax({ url: '/member/' + [_do, _type, _id].join('/'), success: function (result) { if (result.type != 'success') { return alert(result.message); } //修改记数 var num = _this.find('span'), numValue = parseInt(num.html()), active = _this.hasClass('active'); _this.toggleClass('active'); if (num.length) { num.html(numValue + (active ? -1 : 1)); } if ($.inArray(_do, ['like', 'hate']) >= 0) { _this.siblings('[data-do=like],[data-do=hate]').each(function () { var __this = $(this), __do = __this.data('do'), __id = __this.data('id'), __active = __this.hasClass('active'); if (__id != _id) return; // 同一个话题或回复触发 __this.toggleClass('active', __do == _do); var _num = __this.find('span') _numValue = parseInt(_num.html()); if (_num.length) { _num.html(_numValue + (__do != _do ? (_numValue > 0 && __active ? -1 : 0) : 1)); } }); } } }); }); }); ================================================ FILE: frontend/web/robots.txt ================================================ User-agent: * Disallow: ================================================ FILE: frontend/web/uploads/.gitignore ================================================ * !.gitignore ================================================ FILE: frontend/widgets/Alert.php ================================================ getSession()->setFlash('error', 'This is the message'); * \Yii::$app->getSession()->setFlash('success', 'This is the message'); * \Yii::$app->getSession()->setFlash('info', 'This is the message'); * ``` * * Multiple messages could be set as follows: * * ```php * \Yii::$app->getSession()->setFlash('error', ['Error 1', 'Error 2']); * ``` * * @author Kartik Visweswaran * @author Alexander Makarov */ class Alert extends \yii\bootstrap\Widget { /** * @var array the alert types configuration for the flash messages. * This array is setup as $key => $value, where: * - $key is the name of the session flash variable * - $value is the bootstrap alert type (i.e. danger, success, info, warning) */ public $alertTypes = [ 'error' => 'alert-danger', 'danger' => 'alert-danger', 'success' => 'alert-success', 'info' => 'alert-info', 'warning' => 'alert-warning' ]; /** * @var array the options for rendering the close button tag. */ public $closeButton = []; public function init() { parent::init(); $session = \Yii::$app->getSession(); $flashes = $session->getAllFlashes(); $appendCss = isset($this->options['class']) ? ' ' . $this->options['class'] : ''; foreach ($flashes as $type => $data) { if (isset($this->alertTypes[$type])) { $data = (array) $data; foreach ($data as $message) { /* initialize css class for each alert box */ $this->options['class'] = $this->alertTypes[$type] . $appendCss; /* assign unique id to each alert box */ $this->options['id'] = $this->getId() . '-' . $type; echo \yii\bootstrap\Alert::widget([ 'body' => $message, 'closeButton' => $this->closeButton, 'options' => $this->options, ]); } $session->removeFlash($type); } } } } ================================================ FILE: frontend/widgets/Connect.php ================================================ view); if ($this->popupMode) { \Yii::$app->view->registerJs("\$('#" . $this->getId() . "').authchoice();"); } $this->options['id'] = $this->getId(); echo Html::beginTag('div', $this->options); } /** * @inheritdoc */ public function createClientUrl($provider) { if ($this->isConnected($provider)) { return Url::to(['/user/setting/disconnect', 'id' => $this->accounts[$provider->getId()]->id]); } else { return parent::createClientUrl($provider); } } /** * Checks if provider already connected to user. * * @param ClientInterface $provider * @return bool */ public function isConnected(ClientInterface $provider) { return $this->accounts != null && isset($this->accounts[$provider->getId()]); } } ================================================ FILE: frontend/widgets/Nav.php ================================================ * createTime : 15/4/23 下午4:13 * description: */ namespace frontend\widgets; use common\services\UserService; class Nav extends \yii\bootstrap\Widget { public function run() { $notifyCount = UserService::findNotifyCount(); return $this->render('nav', [ 'notifyCount' => $notifyCount, ]); } } ================================================ FILE: frontend/widgets/NewestPost.php ================================================ where(['status' => 1]) ->orderBy(['order' => SORT_ASC, 'created_at' => SORT_DESC]) ->limit(3)->all(); } } ================================================ FILE: frontend/widgets/Node.php ================================================ * createTime : 15/4/25 下午1:42 * description: */ namespace frontend\widgets; use common\models\PostMeta; class Node extends \yii\bootstrap\Widget { public function run() { $nodes = PostMeta::getNodes(); return $this->render('node', [ 'nodes' => $nodes ]); } } ================================================ FILE: frontend/widgets/Panel.php ================================================ * createTime : 15/4/18 下午4:13 * description: */ namespace frontend\widgets; class Panel extends \yii\bootstrap\Widget { public $items = []; public $title = ''; public function run() { $model = [ 'items' => $this->items, 'title' => $this->title, ]; return $this->render('panel', [ 'model' => $model, ]); } } ================================================ FILE: frontend/widgets/TopicSidebar.php ================================================ * createTime : 15/4/18 下午4:13 * description: */ namespace frontend\widgets; use common\helpers\Arr; use common\models\PostMeta; use common\models\RightLink; use frontend\modules\topic\models\Topic; use frontend\modules\user\models\Donate; use yii\helpers\ArrayHelper; use yii\helpers\Url; class TopicSidebar extends \yii\bootstrap\Widget { public $type = 'node'; public $node; public $tags; public function init() { parent::init(); } public function run() { $tipsModel = ArrayHelper::map( RightLink::find()->where(['type' => RightLink::RIGHT_LINK_TYPE_TIPS])->all(), 'content', 'title' ); $tips = $tipsModel ? array_rand($tipsModel) : []; $recommendResources = ArrayHelper::map( RightLink::find()->where(['type' => RightLink::RIGHT_LINK_TYPE_RSOURCES])->all(), 'title', 'url' ); $links = RightLink::find()->where(['type' => RightLink::RIGHT_LINK_TYPE_LINKS])->all(); $sameTopics = []; if ($this->node) { $sameTopics = ArrayHelper::map( Topic::find() ->where('status >= :status', [':status' => Topic::STATUS_ACTIVE]) ->andWhere(['post_meta_id' => $this->node->id, 'type' => 'topic']) ->limit(200)->all(), 'title', function ($e) { return Url::to(['/topic/default/view', 'id' => $e->id]); } ); if (count($sameTopics) > 10) { $sameTopics = Arr::arrayRandomAssoc($sameTopics, 10); } } return $this->render('topicSidebar', [ 'category' => PostMeta::blogCategory(), 'config' => ['type' => $this->type, 'node' => $this->node], 'sameTopics' => $sameTopics, 'tips' => $tips, 'recommendResources' => $recommendResources, 'links' => $links, ]); } } ================================================ FILE: frontend/widgets/views/nav.php ================================================ * createTime : 2015/4/23 17:23 * description: */ use rmrevin\yii\fontawesome\FA; use yii\bootstrap\Nav; use yii\bootstrap\NavBar; use yii\helpers\Html; $module = Yii::$app->controller->module->id; $action = Yii::$app->controller->action->id; $tag = Yii::$app->request->getQueryParam('tag'); $keyword = htmlspecialchars(Yii::$app->request->getQueryParam('keyword')); $node = Yii::$app->request->getQueryParam('node'); $topicActive = ($module == 'topic' && !$tag && $node != 'jobs') ? true : false; $tweetActive = ($module == 'tweet') ? true : false; $topicTagsActive = $action == 'tags' || ($module == 'topic' && $tag) ? true : false; $navActive = ($module == 'nav') ? true : false; $jobsActive = ($node == 'jobs') ? true : false; NavBar::begin([ // 'brandLabel' => Html::img('/images/logo.png'), 'brandLabel' => 'Get√Yii', 'brandUrl' => Yii::$app->homeUrl, 'options' => [ 'class' => 'navbar-white br0 navbar-fixed-top navbar', ], ]); echo Nav::widget([ 'options' => ['class' => 'nav navbar-nav '], 'items' => [ ['label' => '社区', 'url' => ['/topic'], 'active' => $topicActive], ['label' => '招聘', 'url' => ['/topic/default/index', 'node' => 'jobs'], 'active' => $jobsActive], ['label' => '动弹', 'url' => ['/tweet'], 'active' => $tweetActive], ['label' => '标签', 'url' => ['/site/tags'], 'active' => $topicTagsActive], // ['label' => '新手入门', 'url' => ['/site/getstart']], ['label' => '酷站', 'url' => ['/nav'], 'active' => $navActive], ], 'encodeLabels' => false ]); if (Yii::$app->params['setting']['xunsearch']) { echo ''; } if (Yii::$app->user->isGuest) { $menuItems[] = ['label' => '注册', 'url' => ['/site/signup']]; $menuItems[] = ['label' => '登录', 'url' => ['/site/login']]; } else { // 撰写 $menuItems[] = [ 'label' => Html::tag('i', '', ['class' => 'fa fa-bell']) . Html::tag('span', $notifyCount ? $notifyCount : null), 'url' => ['/notification/index'], 'linkOptions' => ['class' => $notifyCount ? 'new' : null], 'options' => ['class' => 'notification-count'], ]; // 个人中心 $menuItems[] = [ 'label' => Yii::$app->user->identity->username, 'items' => [ ['label' => '我的主页', 'url' => ['/user/default']], ['label' => '帐号设置', 'url' => ['/user/setting/profile']], ['label' => '退出', 'url' => ['/site/logout'], 'linkOptions' => ['data-method' => 'post']] ] ]; } echo Nav::widget([ 'encodeLabels' => false, 'options' => ['class' => 'nav navbar-nav navbar-right'], 'items' => $menuItems, 'activateParents' => true, ]); NavBar::end(); ================================================ FILE: frontend/widgets/views/node.php ================================================ * createTime : 15/4/25 下午1:44 * description: */ /** @var \common\models\PostMeta[] $nodes */ ?>

    节点导航

    $value): ?>
    name ?>
      children as $node): ?>
    • name, ['/topic/default/index', 'node' => $node->alias]) ?>
    ================================================ FILE: frontend/widgets/views/panel.php ================================================ * createTime : 15/4/18 下午4:16 * description: */ use yii\helpers\Html; ?>

    ================================================ FILE: frontend/widgets/views/topicSidebar.php ================================================ * createTime : 15/4/18 下午 4:16 * description: */ use yii\helpers\Html; /** @var array $sameTopics */ /** @var array $config */ /** @var array $recommendResources */ /** @var string $tips */ $node = $config['node']; ?>
    ================================================ FILE: init ================================================ #!/usr/bin/env php * * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ if (!extension_loaded('openssl')) { die('The OpenSSL PHP extension is required by Yii2.'); } $params = getParams(); $root = str_replace('\\', '/', __DIR__); $envs = require("$root/environments/index.php"); $envNames = array_keys($envs); echo "Yii Application Initialization Tool v1.0\n\n"; $envName = null; if (empty($params['env']) || $params['env'] === '1') { echo "Which environment do you want the application to be initialized in?\n\n"; foreach ($envNames as $i => $name) { echo " [$i] $name\n"; } echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; $answer = trim(fgets(STDIN)); if (!ctype_digit($answer) || !in_array($answer, range(0, count($envs) - 1))) { echo "\n Quit initialization.\n"; exit(0); } if (isset($envNames[$answer])) { $envName = $envNames[$answer]; } } else { $envName = $params['env']; } if (!in_array($envName, $envNames)) { $envsList = implode(', ', $envNames); echo "\n $envName is not a valid environment. Try one of the following: $envsList. \n"; exit(2); } $env = $envs[$envName]; if (empty($params['env'])) { echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] "; $answer = trim(fgets(STDIN)); if (strncasecmp($answer, 'y', 1)) { echo "\n Quit initialization.\n"; exit(0); } } echo "\n Start initialization ...\n\n"; $files = getFileList("$root/environments/{$env['path']}"); if (isset($env['skipFiles'])) { $skipFiles = $env['skipFiles']; array_walk($skipFiles, function (&$value) use ($env, $root) { $value = "$root/$value"; }); $files = array_diff($files, array_intersect_key($env['skipFiles'], array_filter($skipFiles, 'file_exists'))); } $all = false; foreach ($files as $file) { if (!copyFile($root, "environments/{$env['path']}/$file", $file, $all, $params)) { break; } } $callbacks = ['setCookieValidationKey', 'setWritable', 'setExecutable', 'createSymlink']; foreach ($callbacks as $callback) { if (!empty($env[$callback])) { $callback($root, $env[$callback]); } } echo "\n ... initialization completed.\n\n"; function getFileList($root, $basePath = '') { $files = []; $handle = opendir($root); while (($path = readdir($handle)) !== false) { if ($path === '.git' || $path === '.svn' || $path === '.' || $path === '..') { continue; } $fullPath = "$root/$path"; $relativePath = $basePath === '' ? $path : "$basePath/$path"; if (is_dir($fullPath)) { $files = array_merge($files, getFileList($fullPath, $relativePath)); } else { $files[] = $relativePath; } } closedir($handle); return $files; } function copyFile($root, $source, $target, &$all, $params) { if (!is_file($root . '/' . $source)) { echo " skip $target ($source not exist)\n"; return true; } if (is_file($root . '/' . $target)) { if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { echo " unchanged $target\n"; return true; } if ($all) { echo " overwrite $target\n"; } else { echo " exist $target\n"; echo " ...overwrite? [Yes|No|All|Quit] "; $answer = !empty($params['overwrite']) ? $params['overwrite'] : trim(fgets(STDIN)); if (!strncasecmp($answer, 'q', 1)) { return false; } else { if (!strncasecmp($answer, 'y', 1)) { echo " overwrite $target\n"; } else { if (!strncasecmp($answer, 'a', 1)) { echo " overwrite $target\n"; $all = true; } else { echo " skip $target\n"; return true; } } } } file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); return true; } echo " generate $target\n"; @mkdir(dirname($root . '/' . $target), 0777, true); file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); return true; } function getParams() { $rawParams = []; if (isset($_SERVER['argv'])) { $rawParams = $_SERVER['argv']; array_shift($rawParams); } $params = []; foreach ($rawParams as $param) { if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) { $name = $matches[1]; $params[$name] = isset($matches[3]) ? $matches[3] : true; } else { $params[] = $param; } } return $params; } function setWritable($root, $paths) { foreach ($paths as $writable) { if (is_dir("$root/$writable")) { echo " chmod 0777 $writable\n"; @chmod("$root/$writable", 0777); } else { echo "\n Error. Directory $writable does not exist. \n"; } } } function setExecutable($root, $paths) { foreach ($paths as $executable) { echo " chmod 0755 $executable\n"; @chmod("$root/$executable", 0755); } } function setCookieValidationKey($root, $paths) { foreach ($paths as $file) { echo " generate cookie validation key in $file\n"; $file = $root . '/' . $file; $length = 32; $bytes = openssl_random_pseudo_bytes($length); $key = strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.'); $content = preg_replace('/(("|\')cookieValidationKey("|\')\s*=>\s*)(""|\'\')/', "\\1'$key'", file_get_contents($file)); file_put_contents($file, $content); } } function createSymlink($root, $links) { foreach ($links as $link => $target) { echo " symlink " . $root . "/" . $target . " " . $root . "/" . $link . "\n"; //first removing folders to avoid errors if the folder already exists @rmdir($root . "/" . $link); @symlink($root . "/" . $target, $root . "/" . $link); } } ================================================ FILE: init.bat ================================================ @echo off rem ------------------------------------------------------------- rem Yii command line init script for Windows. rem rem @author Qiang Xue rem @link http://www.yiiframework.com/ rem @copyright Copyright (c) 2008 Yii Software LLC rem @license http://www.yiiframework.com/license/ rem ------------------------------------------------------------- @setlocal set YII_PATH=%~dp0 if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe "%PHP_COMMAND%" "%YII_PATH%init" %* @endlocal ================================================ FILE: requirements.php ================================================ Error'; echo '

    The path to yii framework seems to be incorrect.

    '; echo '

    You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . '.

    '; echo '

    Please refer to the README on how to install Yii.

    '; } require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); $requirementsChecker = new YiiRequirementChecker(); $gdMemo = $imagickMemo = 'Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required for image CAPTCHA.'; $gdOK = $imagickOK = false; if (extension_loaded('imagick')) { $imagick = new Imagick(); $imagickFormats = $imagick->queryFormats('PNG'); if (in_array('PNG', $imagickFormats)) { $imagickOK = true; } else { $imagickMemo = 'Imagick extension should be installed with PNG support in order to be used for image CAPTCHA.'; } } if (extension_loaded('gd')) { $gdInfo = gd_info(); if (!empty($gdInfo['FreeType Support'])) { $gdOK = true; } else { $gdMemo = 'GD extension should be installed with FreeType support in order to be used for image CAPTCHA.'; } } /** * Adjust requirements according to your application specifics. */ $requirements = array( // Database : array( 'name' => 'PDO extension', 'mandatory' => true, 'condition' => extension_loaded('pdo'), 'by' => 'All DB-related classes', ), array( 'name' => 'PDO SQLite extension', 'mandatory' => false, 'condition' => extension_loaded('pdo_sqlite'), 'by' => 'All DB-related classes', 'memo' => 'Required for SQLite database.', ), array( 'name' => 'PDO MySQL extension', 'mandatory' => false, 'condition' => extension_loaded('pdo_mysql'), 'by' => 'All DB-related classes', 'memo' => 'Required for MySQL database.', ), array( 'name' => 'PDO PostgreSQL extension', 'mandatory' => false, 'condition' => extension_loaded('pdo_pgsql'), 'by' => 'All DB-related classes', 'memo' => 'Required for PostgreSQL database.', ), // Cache : array( 'name' => 'Memcache extension', 'mandatory' => false, 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), 'by' => 'MemCache', 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : '' ), array( 'name' => 'APC extension', 'mandatory' => false, 'condition' => extension_loaded('apc'), 'by' => 'ApcCache', ), // CAPTCHA: array( 'name' => 'GD PHP extension with FreeType support', 'mandatory' => false, 'condition' => $gdOK, 'by' => 'Captcha', 'memo' => $gdMemo, ), array( 'name' => 'ImageMagick PHP extension with PNG support', 'mandatory' => false, 'condition' => $imagickOK, 'by' => 'Captcha', 'memo' => $imagickMemo, ), // PHP ini : 'phpExposePhp' => array( 'name' => 'Expose PHP', 'mandatory' => false, 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), 'by' => 'Security reasons', 'memo' => '"expose_php" should be disabled at php.ini', ), 'phpAllowUrlInclude' => array( 'name' => 'PHP allow url include', 'mandatory' => false, 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), 'by' => 'Security reasons', 'memo' => '"allow_url_include" should be disabled at php.ini', ), 'phpSmtp' => array( 'name' => 'PHP mail SMTP', 'mandatory' => false, 'condition' => strlen(ini_get('SMTP')) > 0, 'by' => 'Email sending', 'memo' => 'PHP mail SMTP server required', ), ); $requirementsChecker->checkYii()->check($requirements)->render(); ================================================ FILE: tests/README.md ================================================ This directory contains various tests for the advanced applications. Tests in `codeception` directory are developed with [Codeception PHP Testing Framework](http://codeception.com/). After creating and setting up the advanced application, follow these steps to prepare for the tests: 1. Install Codeception if it's not yet installed: ``` composer global require "codeception/codeception=2.0.*" "codeception/specify=*" "codeception/verify=*" ``` If you've never used Composer for global packages run `composer global status`. It should output: ``` Changed current directory to ``` Then add `/vendor/bin` to you `PATH` environment variable. Now you're able to use `codecept` from command line globally. 2. Install faker extension by running the following from template root directory where `composer.json` is: ``` composer require --dev yiisoft/yii2-faker:* ``` 3. Create `yii2_advanced_tests` database then update it by applying migrations: ``` codeception/bin/yii migrate ``` 4. In order to be able to run acceptance tests you need to start a webserver. The simplest way is to use PHP built in webserver. In the root directory where `common`, `frontend` etc. are execute the following: ``` php -S localhost:8080 ``` 5. Now you can run the tests with the following commands, assuming you are in the `tests/codeception` directory: ``` # frontend tests cd frontend codecept build codecept run # backend tests cd backend codecept build codecept run # etc. ``` If you already have run `codecept build` for each application, you can skip that step and run all tests by a single `codecept run`. ================================================ FILE: tests/codeception/_output/.gitignore ================================================ * !.gitignore ================================================ FILE: tests/codeception/backend/.gitignore ================================================ # these files are auto generated by codeception build /unit/UnitTester.php /functional/FunctionalTester.php /acceptance/AcceptanceTester.php ================================================ FILE: tests/codeception/backend/_bootstrap.php ================================================ wantTo('ensure login page works'); $loginPage = LoginPage::openBy($I); $I->amGoingTo('submit login form with no data'); $loginPage->login('', ''); $I->expectTo('see validations errors'); $I->see('Username cannot be blank.', '.help-block'); $I->see('Password cannot be blank.', '.help-block'); $I->amGoingTo('try to login with wrong credentials'); $I->expectTo('see validations errors'); $loginPage->login('admin', 'wrong'); $I->expectTo('see validations errors'); $I->see('Incorrect username or password.', '.help-block'); $I->amGoingTo('try to login with correct credentials'); $loginPage->login('erau', 'password_0'); $I->expectTo('see that user is logged'); $I->seeLink('Logout (erau)'); $I->dontSeeLink('Login'); $I->dontSeeLink('Signup'); /** Uncomment if using WebDriver * $I->click('Logout (erau)'); * $I->dontSeeLink('Logout (erau)'); * $I->seeLink('Login'); */ ================================================ FILE: tests/codeception/backend/acceptance/_bootstrap.php ================================================ wantTo('ensure login page works'); $loginPage = LoginPage::openBy($I); $I->amGoingTo('submit login form with no data'); $loginPage->login('', ''); $I->expectTo('see validations errors'); $I->see('Username cannot be blank.', '.help-block'); $I->see('Password cannot be blank.', '.help-block'); $I->amGoingTo('try to login with wrong credentials'); $I->expectTo('see validations errors'); $loginPage->login('admin', 'wrong'); $I->expectTo('see validations errors'); $I->see('Incorrect username or password.', '.help-block'); $I->amGoingTo('try to login with correct credentials'); $loginPage->login('erau', 'password_0'); $I->expectTo('see that user is logged'); $I->seeLink('Logout (erau)'); $I->dontSeeLink('Login'); $I->dontSeeLink('Signup'); ================================================ FILE: tests/codeception/backend/functional/_bootstrap.php ================================================ [ 'fixture' => [ 'class' => 'yii\faker\FixtureController', 'fixtureDataPath' => '@tests/codeception/common/fixtures/data', 'templatePath' => '@tests/codeception/common/templates/fixtures', ], ], ] ); $application = new yii\console\Application($config); $exitCode = $application->run(); exit($exitCode); ================================================ FILE: tests/codeception/bin/yii.bat ================================================ @echo off rem ------------------------------------------------------------- rem Yii command line bootstrap script for Windows. rem rem @author Qiang Xue rem @link http://www.yiiframework.com/ rem @copyright Copyright (c) 2008 Yii Software LLC rem @license http://www.yiiframework.com/license/ rem ------------------------------------------------------------- @setlocal set YII_PATH=%~dp0 if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe "%PHP_COMMAND%" "%YII_PATH%yii_acceptance" %* @endlocal ================================================ FILE: tests/codeception/common/.gitignore ================================================ # these files are auto generated by codeception build /unit/UnitTester.php /functional/FunctionalTester.php /acceptance/AcceptanceTester.php ================================================ FILE: tests/codeception/common/_bootstrap.php ================================================ actor->fillField('input[name="LoginForm[username]"]', $username); $this->actor->fillField('input[name="LoginForm[password]"]', $password); $this->actor->click('login-button'); } } ================================================ FILE: tests/codeception/common/_support/FixtureHelper.php ================================================ loadFixtures(); } /** * Method is called after all suite tests run */ public function _afterSuite() { $this->unloadFixtures(); } /** * @inheritdoc */ public function fixtures() { return [ 'user' => [ 'class' => UserFixture::className(), 'dataFile' => '@tests/codeception/common/fixtures/data/init_login.php', ], ]; } } ================================================ FILE: tests/codeception/common/codeception.yml ================================================ namespace: tests\codeception\common actor: Tester paths: tests: . log: _output data: _data helpers: _support settings: bootstrap: _bootstrap.php suite_class: \PHPUnit_Framework_TestSuite colors: true memory_limit: 1024M log: true ================================================ FILE: tests/codeception/common/fixtures/UserFixture.php ================================================ 'erau', 'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI', // password_0 'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne', 'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490', 'created_at' => '1392559490', 'updated_at' => '1392559490', 'email' => 'sfriesen@jenkins.info', ], ]; ================================================ FILE: tests/codeception/common/templates/fixtures/user.php ================================================ getSecurity(); return [ 'username' => $faker->userName, 'email' => $faker->email, 'auth_key' => $security->generateRandomString(), 'password_hash' => $security->generatePasswordHash('password_' . $index), 'password_reset_token' => $security->generateRandomString() . '_' . time(), 'created_at' => time(), 'updated_at' => time(), ]; ================================================ FILE: tests/codeception/common/unit/DbTestCase.php ================================================ 'bayer.hudson', 'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR', //password_0 'password_hash' => '$2y$13$EjaPFBnZOQsHdGuHI.xvhuDp1fHpo8hKRSk6yshqa9c5EG8s3C3lO', 'password_reset_token' => 'ExzkCOaYc1L8IOBs4wdTGGbgNiG3Wz1I_1402312317', 'created_at' => '1402312317', 'updated_at' => '1402312317', 'email' => 'nicole.paucek@schultz.info', ], ]; ================================================ FILE: tests/codeception/common/unit/models/LoginFormTest.php ================================================ [ 'user' => [ 'class' => 'yii\web\User', 'identityClass' => 'common\models\User', ], ], ]); } protected function tearDown() { Yii::$app->user->logout(); parent::tearDown(); } public function testLoginNoUser() { $model = new LoginForm([ 'username' => 'not_existing_username', 'password' => 'not_existing_password', ]); $this->specify('user should not be able to login, when there is no identity', function () use ($model) { expect('model should not login user', $model->login())->false(); expect('user should not be logged in', Yii::$app->user->isGuest)->true(); }); } public function testLoginWrongPassword() { $model = new LoginForm([ 'username' => 'bayer.hudson', 'password' => 'wrong_password', ]); $this->specify('user should not be able to login with wrong password', function () use ($model) { expect('model should not login user', $model->login())->false(); expect('error message should be set', $model->errors)->hasKey('password'); expect('user should not be logged in', Yii::$app->user->isGuest)->true(); }); } public function testLoginCorrect() { $model = new LoginForm([ 'username' => 'bayer.hudson', 'password' => 'password_0', ]); $this->specify('user should be able to login with correct credentials', function () use ($model) { expect('model should login user', $model->login())->true(); expect('error message should not be set', $model->errors)->hasntKey('password'); expect('user should be logged in', Yii::$app->user->isGuest)->false(); }); } /** * @inheritdoc */ public function fixtures() { return [ 'user' => [ 'class' => UserFixture::className(), 'dataFile' => '@tests/codeception/common/unit/fixtures/data/models/user.php' ], ]; } } ================================================ FILE: tests/codeception/common/unit.suite.yml ================================================ # Codeception Test Suite Configuration # suite for unit (internal) tests. # RUN `build` COMMAND AFTER ADDING/REMOVING MODULES. class_name: UnitTester ================================================ FILE: tests/codeception/config/acceptance.php ================================================ 'app-common', 'basePath' => dirname(__DIR__), ] ); ================================================ FILE: tests/codeception/config/config.php ================================================ [ 'db' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host='.$MYSQL_PORT_3306_TCP_ADDR.';dbname='.$MYSQL_TESTS_DB_NAME, 'username' => $MYSQL_USERNAME, 'password' => $MYSQL_PASSWORD, 'charset' => 'utf8mb4', ], 'mailer' => [ 'useFileTransport' => true, ], 'urlManager' => [ 'showScriptName' => true, ], ], ]; ================================================ FILE: tests/codeception/config/console/unit.php ================================================ $value) { $inputType = $field === 'body' ? 'textarea' : 'input'; $this->actor->fillField($inputType . '[name="ContactForm[' . $field . ']"]', $value); } $this->actor->click('contact-button'); } } ================================================ FILE: tests/codeception/frontend/_pages/SignupPage.php ================================================ $value) { $inputType = $field === 'body' ? 'textarea' : 'input'; $this->actor->fillField($inputType . '[name="SignupForm[' . $field . ']"]', $value); } $this->actor->click('signup-button'); } } ================================================ FILE: tests/codeception/frontend/acceptance/AboutCept.php ================================================ wantTo('ensure that about works'); AboutPage::openBy($I); $I->see('About', 'h1'); ================================================ FILE: tests/codeception/frontend/acceptance/ContactCept.php ================================================ wantTo('ensure that contact works'); $contactPage = ContactPage::openBy($I); $I->see('Contact', 'h1'); $I->amGoingTo('submit contact form with no data'); $contactPage->submit([]); $I->expectTo('see validations errors'); $I->see('Contact', 'h1'); $I->see('Name cannot be blank', '.help-block'); $I->see('Email cannot be blank', '.help-block'); $I->see('Subject cannot be blank', '.help-block'); $I->see('Body cannot be blank', '.help-block'); $I->see('The verification code is incorrect', '.help-block'); $I->amGoingTo('submit contact form with not correct email'); $contactPage->submit([ 'name' => 'tester', 'email' => 'tester.email', 'subject' => 'test subject', 'body' => 'test content', 'verifyCode' => 'testme', ]); $I->expectTo('see that email adress is wrong'); $I->dontSee('Name cannot be blank', '.help-block'); $I->see('Email is not a valid email address.', '.help-block'); $I->dontSee('Subject cannot be blank', '.help-block'); $I->dontSee('Body cannot be blank', '.help-block'); $I->dontSee('The verification code is incorrect', '.help-block'); $I->amGoingTo('submit contact form with correct data'); $contactPage->submit([ 'name' => 'tester', 'email' => 'tester@example.com', 'subject' => 'test subject', 'body' => 'test content', 'verifyCode' => 'testme', ]); $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); ================================================ FILE: tests/codeception/frontend/acceptance/HomeCept.php ================================================ wantTo('ensure that home page works'); $I->amOnPage(Yii::$app->homeUrl); $I->see('My Company'); $I->seeLink('About'); $I->click('About'); $I->see('This is the About page.'); ================================================ FILE: tests/codeception/frontend/acceptance/LoginCept.php ================================================ wantTo('ensure login page works'); $loginPage = LoginPage::openBy($I); $I->amGoingTo('submit login form with no data'); $loginPage->login('', ''); $I->expectTo('see validations errors'); $I->see('Username cannot be blank.', '.help-block'); $I->see('Password cannot be blank.', '.help-block'); $I->amGoingTo('try to login with wrong credentials'); $I->expectTo('see validations errors'); $loginPage->login('admin', 'wrong'); $I->expectTo('see validations errors'); $I->see('Incorrect username or password.', '.help-block'); $I->amGoingTo('try to login with correct credentials'); $loginPage->login('erau', 'password_0'); $I->expectTo('see that user is logged'); $I->seeLink('Logout (erau)'); $I->dontSeeLink('Login'); $I->dontSeeLink('Signup'); /** Uncomment if using WebDriver * $I->click('Logout (erau)'); * $I->dontSeeLink('Logout (erau)'); * $I->seeLink('Login'); */ ================================================ FILE: tests/codeception/frontend/acceptance/SignupCest.php ================================================ 'tester.email@example.com', 'username' => 'tester', ]); } /** * This method is called when test fails. * @param \Codeception\Event\FailEvent $event */ public function _fail($event) { } /** * @param \codeception_frontend\AcceptanceTester $I * @param \Codeception\Scenario $scenario */ public function testUserSignup($I, $scenario) { $I->wantTo('ensure that signup works'); $signupPage = SignupPage::openBy($I); $I->see('Signup', 'h1'); $I->see('Please fill out the following fields to signup:'); $I->amGoingTo('submit signup form with no data'); $signupPage->submit([]); $I->expectTo('see validation errors'); $I->see('Username cannot be blank.', '.help-block'); $I->see('Email cannot be blank.', '.help-block'); $I->see('Password cannot be blank.', '.help-block'); $I->amGoingTo('submit signup form with not correct email'); $signupPage->submit([ 'username' => 'tester', 'email' => 'tester.email', 'password' => 'tester_password', ]); $I->expectTo('see that email address is wrong'); $I->dontSee('Username cannot be blank.', '.help-block'); $I->dontSee('Password cannot be blank.', '.help-block'); $I->see('Email is not a valid email address.', '.help-block'); $I->amGoingTo('submit signup form with correct email'); $signupPage->submit([ 'username' => 'tester', 'email' => 'tester.email@example.com', 'password' => 'tester_password', ]); $I->expectTo('see that user logged in'); $I->seeLink('Logout (tester)'); } } ================================================ FILE: tests/codeception/frontend/acceptance/_bootstrap.php ================================================ wantTo('ensure that about works'); AboutPage::openBy($I); $I->see('About', 'h1'); ================================================ FILE: tests/codeception/frontend/functional/ContactCept.php ================================================ wantTo('ensure that contact works'); $contactPage = ContactPage::openBy($I); $I->see('Contact', 'h1'); $I->amGoingTo('submit contact form with no data'); $contactPage->submit([]); $I->expectTo('see validations errors'); $I->see('Contact', 'h1'); $I->see('Name cannot be blank', '.help-block'); $I->see('Email cannot be blank', '.help-block'); $I->see('Subject cannot be blank', '.help-block'); $I->see('Body cannot be blank', '.help-block'); $I->see('The verification code is incorrect', '.help-block'); $I->amGoingTo('submit contact form with not correct email'); $contactPage->submit([ 'name' => 'tester', 'email' => 'tester.email', 'subject' => 'test subject', 'body' => 'test content', 'verifyCode' => 'testme', ]); $I->expectTo('see that email adress is wrong'); $I->dontSee('Name cannot be blank', '.help-block'); $I->see('Email is not a valid email address.', '.help-block'); $I->dontSee('Subject cannot be blank', '.help-block'); $I->dontSee('Body cannot be blank', '.help-block'); $I->dontSee('The verification code is incorrect', '.help-block'); $I->amGoingTo('submit contact form with correct data'); $contactPage->submit([ 'name' => 'tester', 'email' => 'tester@example.com', 'subject' => 'test subject', 'body' => 'test content', 'verifyCode' => 'testme', ]); $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); ================================================ FILE: tests/codeception/frontend/functional/HomeCept.php ================================================ wantTo('ensure that home page works'); $I->amOnPage(Yii::$app->homeUrl); $I->see('My Company'); $I->seeLink('About'); $I->click('About'); $I->see('This is the About page.'); ================================================ FILE: tests/codeception/frontend/functional/LoginCept.php ================================================ wantTo('ensure login page works'); $loginPage = LoginPage::openBy($I); $I->amGoingTo('submit login form with no data'); $loginPage->login('', ''); $I->expectTo('see validations errors'); $I->see('Username cannot be blank.', '.help-block'); $I->see('Password cannot be blank.', '.help-block'); $I->amGoingTo('try to login with wrong credentials'); $I->expectTo('see validations errors'); $loginPage->login('admin', 'wrong'); $I->expectTo('see validations errors'); $I->see('Incorrect username or password.', '.help-block'); $I->amGoingTo('try to login with correct credentials'); $loginPage->login('erau', 'password_0'); $I->expectTo('see that user is logged'); $I->seeLink('Logout (erau)'); $I->dontSeeLink('Login'); $I->dontSeeLink('Signup'); ================================================ FILE: tests/codeception/frontend/functional/SignupCest.php ================================================ 'tester.email@example.com', 'username' => 'tester', ]); } /** * This method is called when test fails. * @param \Codeception\Event\FailEvent $event */ public function _fail($event) { } /** * * @param \codeception_frontend\FunctionalTester $I * @param \Codeception\Scenario $scenario */ public function testUserSignup($I, $scenario) { $I->wantTo('ensure that signup works'); $signupPage = SignupPage::openBy($I); $I->see('Signup', 'h1'); $I->see('Please fill out the following fields to signup:'); $I->amGoingTo('submit signup form with no data'); $signupPage->submit([]); $I->expectTo('see validation errors'); $I->see('Username cannot be blank.', '.help-block'); $I->see('Email cannot be blank.', '.help-block'); $I->see('Password cannot be blank.', '.help-block'); $I->amGoingTo('submit signup form with not correct email'); $signupPage->submit([ 'username' => 'tester', 'email' => 'tester.email', 'password' => 'tester_password', ]); $I->expectTo('see that email address is wrong'); $I->dontSee('Username cannot be blank.', '.help-block'); $I->dontSee('Password cannot be blank.', '.help-block'); $I->see('Email is not a valid email address.', '.help-block'); $I->amGoingTo('submit signup form with correct email'); $signupPage->submit([ 'username' => 'tester', 'email' => 'tester.email@example.com', 'password' => 'tester_password', ]); $I->expectTo('see that user is created'); $I->seeRecord('common\models\User', [ 'username' => 'tester', 'email' => 'tester.email@example.com', ]); $I->expectTo('see that user logged in'); $I->seeLink('Logout (tester)'); } } ================================================ FILE: tests/codeception/frontend/functional/_bootstrap.php ================================================ 'okirlin', 'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv', 'password_hash' => '$2y$13$CXT0Rkle1EMJ/c1l5bylL.EylfmQ39O5JlHJVFpNn618OUS1HwaIi', 'password_reset_token' => 't5GU9NwpuGYSfb7FEZMAxqtuz2PkEvv_' . time(), 'created_at' => '1391885313', 'updated_at' => '1391885313', 'email' => 'brady.renner@rutherford.com', ], [ 'username' => 'troy.becker', 'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS3Lp', 'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2', 'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_' . time(), 'created_at' => '1391885313', 'updated_at' => '1391885313', 'email' => 'nicolas.dianna@hotmail.com', 'status' => '0', ], ]; ================================================ FILE: tests/codeception/frontend/unit/models/ContactFormTest.php ================================================ mailer->fileTransportCallback = function ($mailer, $message) { return 'testing_message.eml'; }; } protected function tearDown() { unlink($this->getMessageFile()); parent::tearDown(); } public function testContact() { $model = new ContactForm(); $model->attributes = [ 'name' => 'Tester', 'email' => 'tester@example.com', 'subject' => 'very important letter subject', 'body' => 'body of current message', ]; $model->sendEmail('admin@example.com'); $this->specify('email should be send', function () { expect('email file should exist', file_exists($this->getMessageFile()))->true(); }); $this->specify('message should contain correct data', function () use ($model) { $emailMessage = file_get_contents($this->getMessageFile()); expect('email should contain user name', $emailMessage)->contains($model->name); expect('email should contain sender email', $emailMessage)->contains($model->email); expect('email should contain subject', $emailMessage)->contains($model->subject); expect('email should contain body', $emailMessage)->contains($model->body); }); } private function getMessageFile() { return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml'; } } ================================================ FILE: tests/codeception/frontend/unit/models/PasswordResetRequestFormTest.php ================================================ mailer->fileTransportCallback = function ($mailer, $message) { return 'testing_message.eml'; }; } protected function tearDown() { @unlink($this->getMessageFile()); parent::tearDown(); } public function testSendEmailWrongUser() { $this->specify('no user with such email, message should not be send', function () { $model = new PasswordResetRequestForm(); $model->email = 'not-existing-email@example.com'; expect('email not send', $model->sendEmail())->false(); }); $this->specify('user is not active, message should not be send', function () { $model = new PasswordResetRequestForm(); $model->email = $this->user[1]['email']; expect('email not send', $model->sendEmail())->false(); }); } public function testSendEmailCorrectUser() { $model = new PasswordResetRequestForm(); $model->email = $this->user[0]['email']; $user = User::findOne(['password_reset_token' => $this->user[0]['password_reset_token']]); expect('email sent', $model->sendEmail())->true(); expect('user has valid token', $user->password_reset_token)->notNull(); $this->specify('message has correct format', function () use ($model) { expect('message file exists', file_exists($this->getMessageFile()))->true(); $message = file_get_contents($this->getMessageFile()); expect('message "from" is correct', $message)->contains(Yii::$app->params['supportEmail']); expect('message "to" is correct', $message)->contains($model->email); }); } public function fixtures() { return [ 'user' => [ 'class' => UserFixture::className(), 'dataFile' => '@tests/codeception/frontend/unit/fixtures/data/models/user.php' ], ]; } private function getMessageFile() { return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml'; } } ================================================ FILE: tests/codeception/frontend/unit/models/ResetPasswordFormTest.php ================================================ user[0]['password_reset_token']); expect('password should be resetted', $form->resetPassword())->true(); } public function fixtures() { return [ 'user' => [ 'class' => UserFixture::className(), 'dataFile' => '@tests/codeception/frontend/unit/fixtures/data/models/user.php' ], ]; } } ================================================ FILE: tests/codeception/frontend/unit/models/SignupFormTest.php ================================================ 'some_username', 'email' => 'some_email@example.com', 'password' => 'some_password', ]); $user = $model->signup(); $this->assertInstanceOf('common\models\User', $user, 'user should be valid'); expect('username should be correct', $user->username)->equals('some_username'); expect('email should be correct', $user->email)->equals('some_email@example.com'); expect('password should be correct', $user->validatePassword('some_password'))->true(); } public function testNotCorrectSignup() { $model = new SignupForm([ 'username' => 'troy.becker', 'email' => 'nicolas.dianna@hotmail.com', 'password' => 'some_password', ]); expect('username and email are in use, user should not be created', $model->signup())->null(); } public function fixtures() { return [ 'user' => [ 'class' => UserFixture::className(), 'dataFile' => '@tests/codeception/frontend/unit/fixtures/data/models/user.php', ], ]; } } ================================================ FILE: tests/codeception/frontend/unit.suite.yml ================================================ # Codeception Test Suite Configuration # suite for unit (internal) tests. # RUN `build` COMMAND AFTER ADDING/REMOVING MODULES. class_name: UnitTester ================================================ FILE: tests/codeception.yml ================================================ include: - codeception/common - codeception/console - codeception/backend - codeception/frontend paths: log: codeception/_output settings: colors: true ================================================ FILE: yii.bat ================================================ @echo off rem ------------------------------------------------------------- rem Yii command line bootstrap script for Windows. rem rem @author Qiang Xue rem @link http://www.yiiframework.com/ rem @copyright Copyright (c) 2008 Yii Software LLC rem @license http://www.yiiframework.com/license/ rem ------------------------------------------------------------- @setlocal set YII_PATH=%~dp0 if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe "%PHP_COMMAND%" "%YII_PATH%yii" %* @endlocal