├── public ├── img │ ├── favicon.ico │ └── social-icons │ │ ├── rss.png │ │ ├── skype.png │ │ ├── facebook.png │ │ ├── twitter.png │ │ ├── youtube.png │ │ └── vkontakte.png ├── codemirror │ ├── codemirror.min.js │ ├── monokai.min.css │ ├── formatting.min.js │ ├── codemirror.min.css │ └── xml.min.js ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── epiceditor │ └── themes │ │ └── editor │ │ └── epic-light.css ├── js │ ├── admin.js │ ├── scrolling-nav.js │ ├── messages_zh.js │ ├── jquery.metisMenu.js │ ├── html5shiv.min.js │ ├── respond.min.js │ ├── jquery.validate.method.js │ ├── jquery.easing.min.js │ └── easykoo.js └── css │ ├── front.css │ ├── zh.txt │ ├── en.txt │ ├── dataTables.bootstrap.css │ └── admin.css ├── common ├── utils_test.go ├── conf.go ├── tsl.go └── utils.go ├── config.ini ├── model ├── base_test.go ├── comment_test.go ├── user_test.go ├── blog_test.go ├── privilege.go ├── role_test.go ├── category_test.go ├── role.go ├── session.go ├── settings.go ├── base.go ├── category.go ├── link.go ├── comment.go ├── feedback.go ├── visit.go ├── page.go └── user.go ├── README.md ├── templates ├── layout │ ├── message.html │ ├── footer.html │ ├── blog_side.html │ ├── front_nav.html │ └── left.html ├── error │ └── 403.html ├── profile │ ├── preferences.html │ └── password.html ├── user │ ├── login.html │ └── register.html ├── index.html ├── blog.html ├── about.html ├── link │ └── edit.html ├── contact.html └── admin │ └── dashboard.html ├── middleware ├── log.go ├── visit.go ├── memory_store.go ├── context.go ├── db_store.go └── file_store.go ├── .gitattributes ├── dbscripts ├── baseFunction.sql ├── baseData_zh.sql ├── baseData.sql └── baseSchema.sql ├── handler ├── commonHandler.go ├── adminHandler.go ├── linkHandler.go ├── feedbackHandler.go └── userHandler.go ├── auth └── auth.go ├── .gitignore └── server.go /public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/favicon.ico -------------------------------------------------------------------------------- /public/img/social-icons/rss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/social-icons/rss.png -------------------------------------------------------------------------------- /public/img/social-icons/skype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/social-icons/skype.png -------------------------------------------------------------------------------- /public/codemirror/codemirror.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/codemirror/codemirror.min.js -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/img/social-icons/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/social-icons/facebook.png -------------------------------------------------------------------------------- /public/img/social-icons/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/social-icons/twitter.png -------------------------------------------------------------------------------- /public/img/social-icons/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/social-icons/youtube.png -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/img/social-icons/vkontakte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/img/social-icons/vkontakte.png -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easykoo/go-blog/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /common/utils_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func Test_Atoa(t *testing.T) { 9 | str := "FirstBlood" 10 | fmt.Println(Atoa(str)) 11 | } 12 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | log_file=false 2 | log_path=./log.txt 3 | http_port=3000 4 | locale=en 5 | 6 | 7 | [db] 8 | server=127.0.0.1 9 | username=root 10 | password=pass 11 | db_name=easy_go 12 | show_sql=true -------------------------------------------------------------------------------- /public/epiceditor/themes/editor/epic-light.css: -------------------------------------------------------------------------------- 1 | html { padding:10px; } 2 | 3 | body { 4 | border:0; 5 | background:#fcfcfc; 6 | font-family:monospace; 7 | font-size:14px; 8 | padding:10px; 9 | line-height:1.35em; 10 | margin:0; 11 | padding:0; 12 | } 13 | -------------------------------------------------------------------------------- /common/conf.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | cfg "github.com/Unknwon/goconfig" 5 | ) 6 | 7 | var Cfg *cfg.ConfigFile 8 | 9 | func SetConfig() { 10 | var err error 11 | Cfg, err = cfg.LoadConfigFile("config.ini") 12 | if err != nil { 13 | Cfg, err = cfg.LoadConfigFile("../config.ini") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /model/base_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "testing" 7 | ) 8 | 9 | func Init() { 10 | SetConfig() 11 | SetLog() 12 | SetEngine() 13 | } 14 | 15 | func Test_GetHotBlog(t *testing.T) { 16 | Init() 17 | blog := new(DbUtil).GetHotBlog() 18 | Log.Debug(blog) 19 | } 20 | -------------------------------------------------------------------------------- /model/comment_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "testing" 7 | ) 8 | 9 | func init() { 10 | SetConfig() 11 | SetLog() 12 | SetEngine() 13 | } 14 | 15 | func Test_GenerateCommentId(t *testing.T) { 16 | // Init() 17 | comment := new(Comment) 18 | comment.Blog.Id = 1 19 | id, err := comment.GenerateSeq() 20 | PanicIf(err) 21 | 22 | Expect(t, id, 1) 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-blog 2 | ======= 3 | build with [Martini](https://github.com/go-martini/martini), [xorm](https://github.com/go-xorm/xorm) & [goconfig](https://github.com/Unknwon/goconfig) 4 | 5 | ## Getting Started 6 | 7 | After installing Mysql, run script files `baseSchema.sql` & `baseData.sql`. 8 | Change the config `config.ini`, then start the program. 9 | 10 | ## Author 11 | 12 | * [Steven](https://github.com/easykoo) 13 | -------------------------------------------------------------------------------- /model/user_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "testing" 7 | ) 8 | 9 | func Test_user(t *testing.T) { 10 | SetEngine() 11 | user := &User{Username: "test4", Password: "11111", Email: "ddd3@ddd.com"} 12 | user.Delete() 13 | err := user.Insert() 14 | PanicIf(err) 15 | 16 | dbUser, err1 := user.GetUser() 17 | PanicIf(err1) 18 | Expect(t, dbUser.Dept.Id, 1) 19 | Expect(t, dbUser.Role.Id, 3) 20 | } 21 | -------------------------------------------------------------------------------- /public/js/admin.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | $('#side-menu').metisMenu(); 4 | 5 | }); 6 | 7 | //Loads the correct sidebar on window load, 8 | //collapses the sidebar on window resize. 9 | $(function () { 10 | $(window).bind("load resize", function () { 11 | if ($(this).width() < 768) { 12 | $('div.sidebar-collapse').addClass('collapse') 13 | } else { 14 | $('div.sidebar-collapse').removeClass('collapse') 15 | } 16 | }) 17 | }) -------------------------------------------------------------------------------- /templates/layout/message.html: -------------------------------------------------------------------------------- 1 | {{with .Messages}} 2 |
3 | 5 | {{range .}} 6 | {{.}} 7 | {{end}} 8 |
9 | {{end}} 10 | {{with .Errors}} 11 | {{range . }} 12 |
13 | 15 | {{.}} 16 |
17 | {{end}} 18 | {{end}} -------------------------------------------------------------------------------- /model/blog_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "testing" 7 | ) 8 | 9 | func Test_SearchTag(t *testing.T) { 10 | SetConfig() 11 | SetLog() 12 | SetEngine() 13 | result, err := orm.Query("select name, count(name) as count from tag group by name order by count desc") 14 | PanicIf(err) 15 | var tagInfoArray []TagInfo 16 | for _, val := range result { 17 | tagInfoArray = append(tagInfoArray, TagInfo{Name: string(val["name"]), Count: ParseInt(string(val["count"]))}) 18 | } 19 | 20 | Log.Debug(tagInfoArray) 21 | } 22 | -------------------------------------------------------------------------------- /middleware/log.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/go-martini/martini" 5 | 6 | . "github.com/easykoo/go-blog/common" 7 | 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | func GetLogger() martini.Handler { 13 | return func(res http.ResponseWriter, req *http.Request, c martini.Context) { 14 | start := time.Now() 15 | Log.Debugf("Started %s %s", req.Method, req.URL.Path) 16 | 17 | rw := res.(martini.ResponseWriter) 18 | c.Next() 19 | 20 | Log.Debugf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /public/css/front.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-top: 100px; 3 | word-wrap: break-word; 4 | } 5 | 6 | @media (max-width: 460px) { 7 | #side { 8 | width: 100%; 9 | } 10 | } 11 | 12 | footer { 13 | padding: 30px 0; 14 | } 15 | 16 | .tag { 17 | margin-top: 5px; 18 | cursor: pointer; 19 | } 20 | 21 | .gray { 22 | color: #ABABAB; 23 | } 24 | 25 | .container { 26 | font-family: Ubuntu, Consolas, "Microsoft YaHei", Verdana, Tahoma, Arial, sans-serif; 27 | font-size: 14px; 28 | } 29 | 30 | .page { 31 | text-align: center; 32 | } 33 | 34 | #editor { 35 | border: 1px solid rgb(204, 204, 204); 36 | border-radius: 4px; 37 | } -------------------------------------------------------------------------------- /common/tsl.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | cfg "github.com/Unknwon/goconfig" 6 | "strings" 7 | ) 8 | 9 | var Tsl *cfg.ConfigFile 10 | 11 | func init() { 12 | Tsl, _ = cfg.LoadConfigFile("messages.ini") 13 | } 14 | 15 | func Translate(lang string, format string) string { 16 | if lang == "" || !strings.Contains(lang, "zh") { 17 | lang = "en" 18 | } 19 | return Tsl.MustValue(lang, format, format) 20 | } 21 | 22 | func Translatef(lang string, format string, args ...interface{}) string { 23 | if lang == "" || !strings.Contains(lang, "zh") { 24 | lang = "en" 25 | } 26 | return fmt.Sprintf(Tsl.MustValue(lang, format, format), args) 27 | } 28 | -------------------------------------------------------------------------------- /dbscripts/baseFunction.sql: -------------------------------------------------------------------------------- 1 | DROP FUNCTION if exists generateCategoryId; 2 | DELIMITER $$ 3 | CREATE FUNCTION generateCategoryId (parentId VARCHAR(20)) RETURNS VARCHAR(50) 4 | begin 5 | declare tempId varchar(20); 6 | if parentId is null or parentId = '' or parentId = 0 then 7 | select max(id) + 1 into tempId from category; 8 | set tempId = ifnull(tempId, '101'); 9 | else 10 | select cast(max(cast(id as unsigned)) + 1 as char(20)) into tempId from category where parent_id = parentId; 11 | if tempId is null then 12 | select concat(parentId, '001') into tempId; 13 | end if; 14 | end if; 15 | RETURN tempId; 16 | END$$ 17 | DELIMITER ; -------------------------------------------------------------------------------- /public/js/scrolling-nav.js: -------------------------------------------------------------------------------- 1 | //jQuery to collapse the navbar on scroll 2 | $(window).scroll(function() { 3 | if ($(".navbar").offset().top > 50) { 4 | $(".navbar-fixed-top").addClass("top-nav-collapse"); 5 | } else { 6 | $(".navbar-fixed-top").removeClass("top-nav-collapse"); 7 | } 8 | }); 9 | 10 | //jQuery for page scrolling feature - requires jQuery Easing plugin 11 | $(function() { 12 | $('.page-scroll a').bind('click', function(event) { 13 | var $anchor = $(this); 14 | $('html, body').stop().animate({ 15 | scrollTop: $($anchor.attr('href')).offset().top 16 | }, 1500, 'easeInOutExpo'); 17 | event.preventDefault(); 18 | }); 19 | }); -------------------------------------------------------------------------------- /public/css/zh.txt: -------------------------------------------------------------------------------- 1 | { 2 | "sProcessing": "处理中...", 3 | "sLengthMenu": "显示 _MENU_ 项结果", 4 | "sZeroRecords": "没有匹配结果", 5 | "sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项", 6 | "sInfoEmpty": "显示第 0 至 0 项结果,共 0 项", 7 | "sInfoFiltered": "(由 _MAX_ 项结果过滤)", 8 | "sInfoPostFix": "", 9 | "sSearch": "搜索:", 10 | "sUrl": "", 11 | "sEmptyTable": "表中数据为空", 12 | "sLoadingRecords": "载入中...", 13 | "sInfoThousands": ",", 14 | "oPaginate": { 15 | "sFirst": "首页", 16 | "sPrevious": "上页", 17 | "sNext": "下页", 18 | "sLast": "末页" 19 | }, 20 | "oAria": { 21 | "sSortAscending": ": 以升序排列此列", 22 | "sSortDescending": ": 以降序排列此列" 23 | } 24 | } -------------------------------------------------------------------------------- /middleware/visit.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/easykoo/sessions" 5 | "github.com/go-martini/martini" 6 | 7 | . "github.com/easykoo/go-blog/common" 8 | "github.com/easykoo/go-blog/model" 9 | 10 | "net/http" 11 | "reflect" 12 | ) 13 | 14 | func RecordVisit() martini.Handler { 15 | return func(s sessions.Session, r *http.Request) { 16 | visit := new(model.Visit) 17 | visit.SessionId = s.GetId() 18 | user := s.Get("SignedUser") 19 | var id int 20 | if user != nil { 21 | if reflect.TypeOf(user).Kind() == reflect.Struct { 22 | id = user.(model.User).Id 23 | } else { 24 | id = user.(*model.User).Id 25 | } 26 | } 27 | visit.User = model.User{Id: id} 28 | visit.Ip = GetRemoteIp(r) 29 | if visit.ExistVisit() { 30 | visit.Update() 31 | } else { 32 | visit.Insert() 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/css/en.txt: -------------------------------------------------------------------------------- 1 | { 2 | "sEmptyTable": "No data available in table", 3 | "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", 4 | "sInfoEmpty": "Showing 0 to 0 of 0 entries", 5 | "sInfoFiltered": "(filtered from _MAX_ total entries)", 6 | "sInfoPostFix": "", 7 | "sInfoThousands": ",", 8 | "sLengthMenu": "Show _MENU_ entries", 9 | "sLoadingRecords": "Loading...", 10 | "sProcessing": "Processing...", 11 | "sSearch": "Search:", 12 | "sZeroRecords": "No matching records found", 13 | "oPaginate": { 14 | "sFirst": "First", 15 | "sLast": "Last", 16 | "sNext": "Next", 17 | "sPrevious": "Previous" 18 | }, 19 | "oAria": { 20 | "sSortAscending": ": activate to sort column ascending", 21 | "sSortDescending": ": activate to sort column descending" 22 | } 23 | } -------------------------------------------------------------------------------- /public/js/messages_zh.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Translated default messages for the jQuery validation plugin. 3 | * Locale: ZH (Chinese, 中文 (Zhōngwén), 汉语, 漢語) 4 | */ 5 | (function($) { 6 | $.extend($.validator.messages, { 7 | required: "必须填写", 8 | remote: "请修正此栏位", 9 | email: "请输入有效的电子邮件", 10 | url: "请输入有效的网址", 11 | date: "请输入有效的日期", 12 | dateISO: "请输入有效的日期 (YYYY-MM-DD)", 13 | number: "请输入正确的数字", 14 | digits: "只可输入数字", 15 | creditcard: "请输入有效的信用卡号码", 16 | equalTo: "你的输入不相同", 17 | extension: "请输入有效的后缀", 18 | maxlength: $.validator.format("最多 {0} 个字"), 19 | minlength: $.validator.format("最少 {0} 个字"), 20 | rangelength: $.validator.format("请输入长度为 {0} 至 {1} 之間的字串"), 21 | range: $.validator.format("请输入 {0} 至 {1} 之间的数值"), 22 | max: $.validator.format("请输入不大于 {0} 的数值"), 23 | min: $.validator.format("请输入不小于 {0} 的数值") 24 | }); 25 | }(jQuery)); -------------------------------------------------------------------------------- /handler/commonHandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/go-martini/martini" 5 | 6 | . "github.com/easykoo/go-blog/common" 7 | "github.com/easykoo/go-blog/middleware" 8 | "github.com/easykoo/go-blog/model" 9 | ) 10 | 11 | func Index(ctx *middleware.Context) { 12 | ctx.HTML(200, "index", ctx) 13 | } 14 | 15 | func About(ctx *middleware.Context) { 16 | ctx.HTML(200, "about", ctx) 17 | } 18 | 19 | func ContactHandler(ctx *middleware.Context, feedback model.Feedback) { 20 | switch ctx.R.Method { 21 | case "POST": 22 | err := feedback.Insert() 23 | PanicIf(err) 24 | ctx.Set("success", true) 25 | ctx.Set("message", Translate(ctx.S.Get("Lang").(string), "message.send.success")) 26 | ctx.JSON(200, ctx.Response) 27 | default: 28 | ctx.HTML(200, "contact", ctx) 29 | } 30 | } 31 | 32 | func LangHandler(ctx *middleware.Context, params martini.Params) { 33 | lang := params["lang"] 34 | ctx.S.Set("Lang", lang) 35 | ctx.Set("success", true) 36 | ctx.JSON(200, ctx.Response) 37 | } 38 | -------------------------------------------------------------------------------- /model/privilege.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | type Privilege struct { 6 | ModuleId int `xorm:"int(11) default 3"` 7 | RoleId int `xorm:"int(11) default 3"` 8 | DeptId int `xorm:"int(11) default 3"` 9 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 10 | CreateDate time.Time `xorm:"datetime created"` 11 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 12 | UpdateDate time.Time `xorm:"datetime updated"` 13 | } 14 | 15 | type Module struct { 16 | Id int `xorm:"int(3) pk not null"` 17 | description string `xorm:"varchar(40) not null"` 18 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 19 | CreateDate time.Time `xorm:"datetime created"` 20 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 21 | UpdateDate time.Time `xorm:"datetime updated"` 22 | } 23 | 24 | func (self *Privilege) CheckModulePrivilege() (bool, error) { 25 | privilege := &Privilege{ModuleId: self.ModuleId, RoleId: self.RoleId, DeptId: self.DeptId} 26 | return orm.Get(privilege) 27 | } 28 | -------------------------------------------------------------------------------- /public/codemirror/monokai.min.css: -------------------------------------------------------------------------------- 1 | .cm-s-monokai.CodeMirror{background:#272822;color:#f8f8f2}.cm-s-monokai div.CodeMirror-selected{background:#49483e!important}.cm-s-monokai .CodeMirror-gutters{background:#272822;border-right:0}.cm-s-monokai .CodeMirror-linenumber{color:#d0d0d0}.cm-s-monokai .CodeMirror-cursor{border-left:1px solid #f8f8f0!important}.cm-s-monokai span.cm-comment{color:#75715e}.cm-s-monokai span.cm-atom{color:#ae81ff}.cm-s-monokai span.cm-number{color:#ae81ff}.cm-s-monokai span.cm-property,.cm-s-monokai span.cm-attribute{color:#a6e22e}.cm-s-monokai span.cm-keyword{color:#f92672}.cm-s-monokai span.cm-string{color:#e6db74}.cm-s-monokai span.cm-variable{color:#a6e22e}.cm-s-monokai span.cm-variable-2{color:#9effff}.cm-s-monokai span.cm-def{color:#fd971f}.cm-s-monokai span.cm-bracket{color:#f8f8f2}.cm-s-monokai span.cm-tag{color:#f92672}.cm-s-monokai span.cm-link{color:#ae81ff}.cm-s-monokai span.cm-error{background:#f92672;color:#f8f8f0}.cm-s-monokai .CodeMirror-activeline-background{background:#373831!important}.cm-s-monokai .CodeMirror-matchingbracket{text-decoration:underline;color:white!important} -------------------------------------------------------------------------------- /model/role_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | type RoleTest struct { 11 | Id int `form:"id" xorm:"int(3) pk not null autoincr"` 12 | Desc string `form:"description" xorm:"varchar(20) not null"` 13 | } 14 | 15 | type UserTest struct { 16 | Id int `form:"id" xorm:"int(11) pk not null autoincr"` 17 | Username string `form:"username" xorm:"varchar(20) not null"` 18 | Role Role `xorm:"role_id int(3) default 3"` 19 | } 20 | 21 | func Test_RoleTest(t *testing.T) { 22 | SetEngine() 23 | err := orm.DropTables(&RoleTest{}, &UserTest{}) 24 | if err != nil { 25 | fmt.Println(err) 26 | return 27 | } 28 | 29 | err = orm.CreateTables(&RoleTest{}, &UserTest{}) 30 | if err != nil { 31 | fmt.Println(err) 32 | return 33 | } 34 | 35 | _, err = orm.Insert(&RoleTest{Id: 1, Desc: "test1"}, &UserTest{Id: 1, Username: "username"}) 36 | if err != nil { 37 | fmt.Println(err) 38 | return 39 | } 40 | 41 | userTest := UserTest{} 42 | _, err = orm.Id(1).Get(&UserTest{}) 43 | if err != nil { 44 | fmt.Println(err) 45 | return 46 | } 47 | fmt.Println(userTest) 48 | 49 | Expect(t, userTest.Role.Id, 1) 50 | } 51 | -------------------------------------------------------------------------------- /templates/layout/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /model/category_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func Test_Category(t *testing.T) { 11 | SetConfig() 12 | SetLog() 13 | SetEngine() 14 | err := orm.DropTables(&Category{}) 15 | if err != nil { 16 | fmt.Println(err) 17 | return 18 | } 19 | 20 | err = orm.CreateTables(&Category{}) 21 | if err != nil { 22 | fmt.Println(err) 23 | return 24 | } 25 | 26 | _, err = orm.Insert(&Category{Id: 1, Description: "test1"}, &Category{Id: 2, Description: "test2"}, &Category{Id: 3, Description: "test3"}) 27 | if err != nil { 28 | fmt.Println(err) 29 | return 30 | } 31 | 32 | category := Category{} 33 | _, err = orm.Id(1).Get(&category) 34 | if err != nil { 35 | fmt.Println(err) 36 | return 37 | } 38 | fmt.Println(category) 39 | 40 | Expect(t, category.Id, 1) 41 | } 42 | 43 | func Test_SearchCategory(t *testing.T) { 44 | SetConfig() 45 | SetLog() 46 | SetEngine() 47 | category := new(Category) 48 | blogList, total, err := category.SearchByPage() 49 | Log.Debug(blogList, total, err) 50 | } 51 | 52 | func Test_GenerateCategoryId(t *testing.T) { 53 | SetConfig() 54 | SetLog() 55 | SetEngine() 56 | category := new(Category) 57 | id, err := category.GenerateCategoryId(0) 58 | PanicIf(err) 59 | 60 | Expect(t, id, 101) 61 | } 62 | -------------------------------------------------------------------------------- /model/role.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Role struct { 8 | Id int `form:"roleId" xorm:"int(3) pk not null autoincr"` 9 | Description string `form:"description" xorm:"varchar(20) not null"` 10 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 11 | CreateDate time.Time `xorm:"datetime created"` 12 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 13 | UpdateDate time.Time `xorm:"datetime updated"` 14 | Version int `form:"version" xorm:"int(11) version"` 15 | } 16 | 17 | func (self *Role) GetRoleById(id int) (*Role, error) { 18 | role := &Role{Id: id} 19 | _, err := orm.Get(role) 20 | return role, err 21 | } 22 | 23 | type Dept struct { 24 | Id int `form:"deptId" xorm:"int(3) pk not null autoincr"` 25 | Description string `form:"description" xorm:"varchar(20) not null"` 26 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 27 | CreateDate time.Time `xorm:"datetime created"` 28 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 29 | UpdateDate time.Time `xorm:"datetime updated"` 30 | Version int `form:"version" xorm:"int(11) version"` 31 | } 32 | 33 | func (self *Dept) GetRoleById(id int) (*Dept, error) { 34 | dept := &Dept{Id: id} 35 | _, err := orm.Get(dept) 36 | return dept, err 37 | } 38 | -------------------------------------------------------------------------------- /templates/error/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | No Permission 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 |
18 |
19 |

403 - No Permission

20 |
21 |
22 |

23 | Please contact the administrator. 24 | Go Back 25 |
26 |

27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /model/session.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "time" 7 | ) 8 | 9 | type SessionInfo struct { 10 | Id string `xorm:"varchar(60) pk not null "` 11 | Content string `xorm:"varchar(15) null"` 12 | Age int `xorm:"int(9)"` 13 | CreateDate time.Time `xorm:"datetime created"` 14 | UpdateDate time.Time `xorm:"datetime updated"` 15 | } 16 | 17 | func (self *SessionInfo) Exist() (exist bool) { 18 | exist, err := orm.Get(&SessionInfo{Id: self.Id}) 19 | PanicIf(err) 20 | return 21 | } 22 | 23 | func (self *SessionInfo) GetSessionInfo() (sessionInfo SessionInfo) { 24 | _, err := orm.Id(self.Id).Get(&sessionInfo) 25 | PanicIf(err) 26 | return 27 | } 28 | 29 | func (self *SessionInfo) Insert() error { 30 | _, err := orm.InsertOne(self) 31 | Log.Info("SessionInfo ", self.Id, " inserted") 32 | return err 33 | } 34 | 35 | func (self *SessionInfo) Update() error { 36 | _, err := orm.Id(self.Id).Update(self) 37 | Log.Info("SessionInfo ", self.Id, " updated") 38 | return err 39 | } 40 | 41 | func (self *SessionInfo) Delete() error { 42 | _, err := orm.Delete(self) 43 | Log.Info("SessionInfo ", self.Id, " deleted") 44 | return err 45 | } 46 | 47 | func (self *SessionInfo) RemoveExpiredSession() (err error) { 48 | _, err = orm.Exec("delete from session_info where UNIX_TIMESTAMP(now()) >= age + UNIX_TIMESTAMP(update_date)") 49 | return 50 | } 51 | -------------------------------------------------------------------------------- /handler/adminHandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | "github.com/easykoo/go-blog/middleware" 6 | "github.com/easykoo/go-blog/model" 7 | ) 8 | 9 | func DashboardHandler(ctx *middleware.Context) { 10 | visit := new(model.Visit) 11 | visit.SetPageActive(true) 12 | visit.SetPageSize(10) 13 | pageNo := ParseInt(ctx.R.FormValue("page")) 14 | visit.SetPageNo(pageNo) 15 | visit.AddSortProperty("create_date", "desc") 16 | visitList, total, err := visit.SearchByPage() 17 | PanicIf(err) 18 | 19 | visit.SetTotalRecord(total) 20 | visit.Result = visitList 21 | ctx.Set("Visit", visit) 22 | ctx.HTML(200, "admin/dashboard", ctx) 23 | } 24 | 25 | func SettingsHandler(ctx *middleware.Context, settings model.Settings) { 26 | if ctx.R.Method == "POST" { 27 | err := settings.Update() 28 | PanicIf(err) 29 | dbSettings := model.GetSettings() 30 | ctx.AddMessage(Translate(ctx.S.Get("Lang").(string), "message.change.success")) 31 | ctx.S.Set("Settings", dbSettings) 32 | } 33 | user := &model.User{} 34 | users, err := user.SelectAll() 35 | PanicIf(err) 36 | ctx.Set("Users", users) 37 | 38 | ctx.HTML(200, "admin/settings", ctx) 39 | } 40 | 41 | func AboutHandler(ctx *middleware.Context) { 42 | settings := model.GetSettings() 43 | about := ctx.R.FormValue("about") 44 | settings.About = about 45 | err := settings.Update() 46 | PanicIf(err) 47 | dbSettings := model.GetSettings() 48 | ctx.S.Set("Settings", dbSettings) 49 | 50 | ctx.Redirect("/about") 51 | } 52 | -------------------------------------------------------------------------------- /model/settings.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "time" 7 | ) 8 | 9 | type Settings struct { 10 | Id int `form:"settingsId" xorm:"int(11) pk not null autoincr"` 11 | AppName string `form:"appName" xorm:"varchar(45) not null"` 12 | About string `form:"about" xorm:"blob not null"` 13 | Owner User `form:"owner_id" json:"owner_id" xorm:"owner_id"` 14 | Keywords string `form:"keywords" xorm:"varchar(100) default null"` 15 | Description string `form:"description" xorm:"varchar(100) default null"` 16 | CreateUser string `xorm:"varchar(20) default null"` 17 | CreateDate time.Time `xorm:"datetime created"` 18 | UpdateUser string `xorm:"varchar(20) default null"` 19 | UpdateDate time.Time `xorm:"datetime updated"` 20 | Version int `form:"version" xorm:"int(11) version"` 21 | Page `xorm:"-"` 22 | } 23 | 24 | func GetSettings() *Settings { 25 | settings := &Settings{Id: 1} 26 | _, err := orm.Get(settings) 27 | PanicIf(err) 28 | return settings 29 | } 30 | 31 | func (self *Settings) Insert() error { 32 | _, err := orm.InsertOne(self) 33 | Log.Info("Settings ", self.Id, " inserted") 34 | return err 35 | } 36 | 37 | func (self *Settings) Update() error { 38 | _, err := orm.Id(self.Id).Update(self) 39 | Log.Info("Settings ", self.Id, " updated!") 40 | return err 41 | } 42 | 43 | func (self *Settings) Delete() error { 44 | _, err := orm.Delete(self) 45 | Log.Info("Settings ", self.Id, " deleted") 46 | return err 47 | } 48 | -------------------------------------------------------------------------------- /public/js/jquery.metisMenu.js: -------------------------------------------------------------------------------- 1 | ;(function ($, window, document, undefined) { 2 | 3 | var pluginName = "metisMenu", 4 | defaults = { 5 | toggle: true 6 | }; 7 | 8 | function Plugin(element, options) { 9 | this.element = element; 10 | this.settings = $.extend({}, defaults, options); 11 | this._defaults = defaults; 12 | this._name = pluginName; 13 | this.init(); 14 | } 15 | 16 | Plugin.prototype = { 17 | init: function () { 18 | 19 | var $this = $(this.element), 20 | $toggle = this.settings.toggle; 21 | 22 | $this.find('li.active').has('ul').children('ul').addClass('collapse in'); 23 | $this.find('li').not('.active').has('ul').children('ul').addClass('collapse'); 24 | 25 | $this.find('li').has('ul').children('a').on('click', function (e) { 26 | e.preventDefault(); 27 | 28 | $(this).parent('li').toggleClass('active').children('ul').collapse('toggle'); 29 | 30 | if ($toggle) { 31 | $(this).parent('li').siblings().removeClass('active').children('ul.in').collapse('hide'); 32 | } 33 | }); 34 | } 35 | }; 36 | 37 | $.fn[ pluginName ] = function (options) { 38 | return this.each(function () { 39 | if (!$.data(this, "plugin_" + pluginName)) { 40 | $.data(this, "plugin_" + pluginName, new Plugin(this, options)); 41 | } 42 | }); 43 | }; 44 | 45 | })(jQuery, window, document); 46 | -------------------------------------------------------------------------------- /model/base.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | _ "github.com/go-sql-driver/mysql" 5 | "github.com/go-xorm/xorm" 6 | 7 | . "github.com/easykoo/go-blog/common" 8 | "time" 9 | ) 10 | 11 | var orm *xorm.Engine 12 | 13 | func SetEngine() *xorm.Engine { 14 | Log.Info("db initializing...") 15 | var err error 16 | server := Cfg.MustValue("db", "server", "127.0.0.1") 17 | username := Cfg.MustValue("db", "username", "root") 18 | password := Cfg.MustValue("db", "password", "pass") 19 | dbName := Cfg.MustValue("db", "db_name", "go_display") 20 | orm, err = xorm.NewEngine("mysql", username+":"+password+"@tcp("+server+":3306)/"+dbName+"?charset=utf8") 21 | PanicIf(err) 22 | orm.TZLocation = time.Local 23 | orm.ShowSQL = Cfg.MustBool("db", "show_sql", false) 24 | orm.Logger = xorm.NewSimpleLogger(Log.GetWriter()) 25 | return orm 26 | } 27 | 28 | type DbUtil struct{} 29 | 30 | func (self *DbUtil) GetRecentComments() (comments []Comment) { 31 | err := orm.OrderBy("create_date desc").Limit(5, 0).Find(&comments, &Comment{}) 32 | PanicIf(err) 33 | return 34 | } 35 | 36 | func (self *DbUtil) GetHotBlog() (blog []Blog) { 37 | result, err := orm.Query("select * from blog b, (select blog_id, count(*) count from comment group by blog_id order by count desc limit 0,5) t where b.id = t.blog_id order by t.count desc, b.create_date desc") 38 | PanicIf(err) 39 | for _, val := range result { 40 | b := Blog{Id: int(ParseInt(string(val["id"]))), Title: string(val["title"])} 41 | blog = append(blog, b) 42 | } 43 | return 44 | } 45 | 46 | func (self *DbUtil) GetAllLinks() (links []Link) { 47 | err := orm.OrderBy("create_date desc").Find(&links, &Link{}) 48 | PanicIf(err) 49 | return links 50 | } 51 | -------------------------------------------------------------------------------- /model/category.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "time" 7 | ) 8 | 9 | type Category struct { 10 | Id int `form:"id" xorm:"int(3) pk not null autoincr"` 11 | Description string `form:"description" xorm:"varchar(20) not null"` 12 | ParentId int `form:"parentId" xorm:"int(3)"` 13 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 14 | CreateDate time.Time `xorm:"datetime created"` 15 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 16 | UpdateDate time.Time `xorm:"datetime updated"` 17 | Version int `form:"version" xorm:"int(11) version"` 18 | Page `xorm:"-"` 19 | } 20 | 21 | func (self *Category) insert() (int64, error) { 22 | id, err := self.GenerateCategoryId(self.ParentId) 23 | PanicIf(err) 24 | self.Id = id 25 | return orm.Insert(self) 26 | } 27 | 28 | func (self *Category) GetCategoryById(id int) (*Category, error) { 29 | category := &Category{Id: id} 30 | _, err := orm.Get(category) 31 | return category, err 32 | } 33 | 34 | func (self *Category) SearchByPage() ([]Category, int, error) { 35 | total, err := orm.Count(self) 36 | var category []Category 37 | 38 | session := orm.NewSession() 39 | defer session.Close() 40 | if len(self.GetSortProperties()) > 0 { 41 | session = session.OrderBy(self.GetSortProperties()[0].Column + " " + self.GetSortProperties()[0].Direction) 42 | } 43 | err = session.Limit(self.GetPageSize(), self.GetDisplayStart()).Find(&category, self) 44 | return category, int(total), err 45 | } 46 | 47 | func (self *Category) GenerateCategoryId(parentId int) (int, error) { 48 | result, err := orm.Query("select generateCategoryId(?) as id", IntString(parentId)) 49 | return ParseInt(string(result[0]["id"])), err 50 | } 51 | -------------------------------------------------------------------------------- /auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/go-martini/martini" 5 | 6 | . "github.com/easykoo/go-blog/common" 7 | "github.com/easykoo/go-blog/middleware" 8 | "github.com/easykoo/go-blog/model" 9 | 10 | "reflect" 11 | ) 12 | 13 | const ( 14 | SignInRequired = 9 15 | Module_Admin = iota 16 | Module_Account 17 | Module_Feedback 18 | Module_News 19 | Module_Product 20 | Module_Blog 21 | Module_Link 22 | ) 23 | 24 | func AuthRequest(req interface{}) martini.Handler { 25 | return func(ctx *middleware.Context) { 26 | Log.Info("Checking privilege: ", ctx.R.RequestURI) 27 | switch req { 28 | case SignInRequired: 29 | Log.Info("Checking style: ", "SignInRequired") 30 | if user := ctx.S.Get("SignedUser"); user != nil { 31 | Log.Info("Pass!") 32 | return 33 | } 34 | ctx.Redirect("/user/login") 35 | return 36 | default: 37 | Log.Info("Checking style: ", "Module ", req.(int)) 38 | if user := ctx.S.Get("SignedUser"); user != nil { 39 | if reflect.TypeOf(req).Kind() == reflect.Int { 40 | if CheckPermission(user, req.(int)) { 41 | Log.Info("Pass!") 42 | return 43 | } 44 | ctx.HTML(403, "error/403", ctx) 45 | return 46 | } 47 | } else { 48 | ctx.Redirect("/user/login") 49 | return 50 | } 51 | ctx.HTML(403, "error/403", ctx) 52 | return 53 | } 54 | } 55 | } 56 | 57 | func CheckPermission(user interface{}, module int) bool { 58 | if reflect.TypeOf(user).Kind() == reflect.Struct { 59 | val := user.(model.User) 60 | privilege := &model.Privilege{ModuleId: module, RoleId: val.Role.Id, DeptId: val.Dept.Id} 61 | exist, err := privilege.CheckModulePrivilege() 62 | PanicIf(err) 63 | return exist 64 | } else { 65 | val := user.(*model.User) 66 | privilege := &model.Privilege{ModuleId: module, RoleId: val.Role.Id, DeptId: val.Dept.Id} 67 | exist, err := privilege.CheckModulePrivilege() 68 | PanicIf(err) 69 | return exist 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /model/link.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | "time" 6 | ) 7 | 8 | type Link struct { 9 | Id int `form:"id" xorm:"int(3) pk not null autoincr"` 10 | Description string `form:"description" xorm:"varchar(40) not null"` 11 | Url string `form:"url" xorm:"varchar(80) not null"` 12 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 13 | CreateDate time.Time `xorm:"datetime created"` 14 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 15 | UpdateDate time.Time `xorm:"datetime updated"` 16 | Version int `form:"version" xorm:"int(11) version"` 17 | Page `xorm:"-"` 18 | } 19 | 20 | func (self *Link) Insert() error { 21 | session := orm.NewSession() 22 | defer session.Close() 23 | _, err := session.InsertOne(self) 24 | Log.Info("Link ", self.Id, " inserted") 25 | return err 26 | } 27 | 28 | func (self *Link) Update() error { 29 | session := orm.NewSession() 30 | defer session.Close() 31 | _, err := session.Id(self.Id).Update(self) 32 | Log.Info("Link ", self.Id, " updated!") 33 | return err 34 | } 35 | 36 | func (self *Link) Delete() error { 37 | session := orm.NewSession() 38 | defer session.Close() 39 | _, err := session.Delete(self) 40 | Log.Info("Link ", self.Id, " deleted") 41 | return err 42 | } 43 | 44 | func (self *Link) GetLinkById() (*Link, error) { 45 | link := &Link{Id: self.Id} 46 | _, err := orm.Get(link) 47 | return link, err 48 | } 49 | 50 | func (self *Link) GetLink() error { 51 | _, err := orm.Id(self.Id).Get(self) 52 | return err 53 | } 54 | 55 | func (self *Link) DeleteLinkArray(array []int) error { 56 | _, err := orm.In("id", array).Delete(&Link{}) 57 | Log.Info("Link Array: ", array, " deleted") 58 | return err 59 | } 60 | 61 | func (self *Link) SearchByPage() ([]Link, int, error) { 62 | total, err := orm.Count(self) 63 | var link []Link 64 | err = orm.OrderBy(self.GetSortProperties()[0].Column+" "+self.GetSortProperties()[0].Direction).Limit(self.GetPageSize(), self.GetDisplayStart()).Find(&link, self) 65 | return link, int(total), err 66 | } 67 | -------------------------------------------------------------------------------- /common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "io" 7 | "math/rand" 8 | "net/http" 9 | "os" 10 | "reflect" 11 | "strconv" 12 | "strings" 13 | "testing" 14 | "time" 15 | ) 16 | 17 | var Log *Logger 18 | 19 | func SetLog() { 20 | var out io.Writer 21 | if Cfg.MustBool("", "log_file", false) { 22 | f, _ := os.OpenFile(Cfg.MustValue("", "log_path", "./log.txt"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777) 23 | out = io.MultiWriter(f) 24 | } else { 25 | out = os.Stdout 26 | } 27 | Log = New(out, "[go-blog]", Lshortfile|Ldate|Lmicroseconds) 28 | } 29 | 30 | func PanicIf(err error) { 31 | if err != nil { 32 | panic(err) 33 | } 34 | } 35 | 36 | func ParseInt(value string) int { 37 | if value == "" { 38 | return 0 39 | } 40 | val, _ := strconv.Atoi(value) 41 | return val 42 | } 43 | 44 | func IntString(value int) string { 45 | return strconv.Itoa(value) 46 | } 47 | 48 | func Md5(str string) string { 49 | h := md5.New() 50 | h.Write([]byte(str)) 51 | return hex.EncodeToString(h.Sum(nil)) 52 | } 53 | 54 | func Atoa(str string) string { 55 | var result string 56 | for i := 0; i < len(str); i++ { 57 | c := rune(str[i]) 58 | if 'A' <= c && c <= 'Z' && i > 0 { 59 | result = result + "_" + strings.ToLower(string(str[i])) 60 | } else { 61 | result = result + string(str[i]) 62 | } 63 | } 64 | return result 65 | } 66 | 67 | func GetRemoteIp(r *http.Request) (ip string) { 68 | ip = r.Header.Get("X-Real-Ip") 69 | if ip == "" { 70 | ip = r.RemoteAddr 71 | } 72 | ip = strings.Split(ip, ":")[0] 73 | if len(ip) < 7 || ip == "127.0.0.1" { 74 | ip = "localhost" 75 | } 76 | return 77 | } 78 | 79 | /* Test Helpers */ 80 | func Expect(t *testing.T, a interface{}, b interface{}) { 81 | if a != b { 82 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 83 | } 84 | } 85 | 86 | func GenToken() string { 87 | nano := time.Now().UnixNano() 88 | rand.Seed(nano) 89 | rndNum := rand.Int63() 90 | uuid := Md5(Md5(strconv.FormatInt(nano, 10)) + Md5(strconv.FormatInt(rndNum, 10))) 91 | return uuid 92 | } 93 | -------------------------------------------------------------------------------- /model/comment.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "time" 7 | ) 8 | 9 | type Comment struct { 10 | Blog Blog `xorm:"blog_id int(11) pk not null"` 11 | Seq int `xorm:"int(5) pk not null"` 12 | Name string `form:"name" xorm:"varchar(20) null"` 13 | Www string `form:"www" xorm:"varchar(45) null"` 14 | Email string `form:"email" xorm:"varchar(45) null"` 15 | Content string `form:"content" xorm:"varchar(150) not null"` 16 | ParentSeq int `form:"prentSeq" xorm:"int(5) null"` 17 | Ip string `xorm:"varchar(15) null"` 18 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 19 | CreateDate time.Time `xorm:"datetime created"` 20 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 21 | UpdateDate time.Time `xorm:"datetime updated"` 22 | Version int `form:"version" xorm:"int(11) version"` 23 | Page `xorm:"-"` 24 | } 25 | 26 | func (self *Comment) GenerateSeq() (int, error) { 27 | result, err := orm.Query("select max(seq)+1 as seq from comment where blog_id = ?", self.Blog.Id) 28 | seq := ParseInt(string(result[0]["seq"])) 29 | if seq < 1 { 30 | seq = 1 31 | } 32 | return seq, err 33 | } 34 | 35 | func (self *Comment) Insert() error { 36 | seq, err := self.GenerateSeq() 37 | self.Seq = seq 38 | _, err = orm.InsertOne(self) 39 | Log.Info("Comment ", self.Blog.Id, " ", self.Seq, " inserted") 40 | return err 41 | } 42 | 43 | func (self *Comment) Update() error { 44 | _, err := orm.Update(self) 45 | Log.Info("Comment ", self.Blog.Id, " ", self.Seq, " updated") 46 | return err 47 | } 48 | 49 | func (self *Comment) Delete() error { 50 | _, err := orm.Delete(self) 51 | Log.Info("Comment ", self.Blog.Id, " ", self.Seq, " deleted") 52 | return err 53 | } 54 | 55 | func (self *Comment) SearchByPage() ([]Comment, int64, error) { 56 | total, err := orm.Count(self) 57 | var comment []Comment 58 | err = orm.OrderBy(self.GetSortProperties()[0].Column+" "+self.GetSortProperties()[0].Direction).Limit(self.GetPageSize(), self.GetDisplayStart()).Find(&comment, self) 59 | return comment, total, err 60 | } 61 | -------------------------------------------------------------------------------- /model/feedback.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | type Feedback struct { 11 | Id int `xorm:"int(11) pk not null autoincr"` 12 | Name string `form:"name" xorm:"varchar(20) not null"` 13 | Email string `form:"email" xorm:"varchar(45) unique"` 14 | Content string `form:"content" xorm:"varchar(45) unique"` 15 | Viewed bool `xorm:"tinyint(1) default 0"` 16 | CreateDate time.Time `xorm:"datetime created"` 17 | ViewDate time.Time `xorm:"datetime updated"` 18 | Page `xorm:"-"` 19 | } 20 | 21 | func (self *Feedback) Insert() error { 22 | _, err := orm.InsertOne(self) 23 | Log.Info("Feedback ", self.Id, " inserted") 24 | return err 25 | } 26 | 27 | func (self *Feedback) Delete() error { 28 | _, err := orm.Delete(self) 29 | Log.Info("Feedback ", self.Id, " deleted") 30 | return err 31 | } 32 | 33 | func (self *Feedback) SetViewed(view bool) error { 34 | var err error 35 | _, err = orm.Id(self.Id).UseBool("viewed").Update(&Feedback{Viewed: view}) 36 | return err 37 | } 38 | 39 | func (self *Feedback) DeleteFeedbackArray(array []int) error { 40 | _, err := orm.In("id", array).Delete(&Feedback{}) 41 | sql := "delete from `feedback` where id in (" 42 | for index, val := range array { 43 | sql += strconv.Itoa(val) 44 | if index < len(array)-1 { 45 | sql += "," 46 | } 47 | } 48 | sql += ")" 49 | _, err = orm.Exec(sql) 50 | Log.Info("Feedback array: ", array, " deleted") 51 | return err 52 | } 53 | 54 | func (self *Feedback) Info() ([]Feedback, int64, error) { 55 | total, err := orm.UseBool("viewed").MustCols("viewed").Count(self) 56 | var feedback []Feedback 57 | err = orm.UseBool("viewed").MustCols("viewed").OrderBy("create_date desc").Limit(5, 0).Find(&feedback, self) 58 | return feedback, total, err 59 | } 60 | 61 | func (self *Feedback) SearchByPage() ([]Feedback, int64, error) { 62 | total, err := orm.Count(self) 63 | var feedback []Feedback 64 | err = orm.OrderBy(self.GetSortProperties()[0].Column+" "+self.GetSortProperties()[0].Direction).Limit(self.GetPageSize(), self.GetDisplayStart()).Find(&feedback, self) 65 | return feedback, total, err 66 | } 67 | -------------------------------------------------------------------------------- /model/visit.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | 6 | "time" 7 | ) 8 | 9 | type Visit struct { 10 | SessionId string `xorm:"varchar(60) pk not null "` 11 | Ip string `xorm:"varchar(15) null"` 12 | User User `json:"user_id" xorm:"user_id"` 13 | CreateDate time.Time `xorm:"datetime created"` 14 | Page `xorm:"-"` 15 | } 16 | 17 | type Statistics struct { 18 | TotalCount int 19 | MonthCount int 20 | DayCount int 21 | } 22 | 23 | func (self *Visit) Exist() (exist bool) { 24 | exist, err := orm.Get(self) 25 | PanicIf(err) 26 | return 27 | } 28 | 29 | func (self *Visit) ExistVisit() (exist bool) { 30 | exist, err := orm.Get(&Visit{SessionId: self.SessionId}) 31 | PanicIf(err) 32 | return 33 | } 34 | 35 | func (self *Visit) GetVisit() (visit Visit) { 36 | _, err := orm.Id(self.SessionId).Get(&visit) 37 | PanicIf(err) 38 | return 39 | } 40 | 41 | func (self *Visit) Insert() error { 42 | _, err := orm.InsertOne(self) 43 | Log.Info("Visit ", self.SessionId, " inserted") 44 | return err 45 | } 46 | 47 | func (self *Visit) Update() error { 48 | _, err := orm.Id(self.SessionId).Update(self) 49 | Log.Info("Visit ", self.SessionId, " updated") 50 | return err 51 | } 52 | 53 | func (self *Visit) Delete() error { 54 | _, err := orm.Delete(self) 55 | Log.Info("Visit ", self.SessionId, " deleted") 56 | return err 57 | } 58 | 59 | func (self *Visit) SearchByPage() ([]Visit, int, error) { 60 | total, err := orm.Count(self) 61 | var visit []Visit 62 | err = orm.OrderBy(self.GetSortProperties()[0].Column+" "+self.GetSortProperties()[0].Direction).Limit(self.GetPageSize(), self.GetDisplayStart()).Find(&visit, self) 63 | return visit, int(total), err 64 | } 65 | 66 | func (self *Visit) GetStatistics() (stat Statistics) { 67 | 68 | total, err := orm.Count(&Visit{}) 69 | PanicIf(err) 70 | stat.TotalCount = int(total) 71 | 72 | monthCount, err := orm.Where("date_format(create_date,'%Y-%m')=date_format(now(),'%Y-%m')").Count(&Visit{}) 73 | PanicIf(err) 74 | stat.MonthCount = int(monthCount) 75 | 76 | dayCount, err := orm.Where(" datediff(create_date,NOW()) = 0").Count(&Visit{}) 77 | PanicIf(err) 78 | stat.DayCount = int(dayCount) 79 | 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /templates/profile/preferences.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.preferences"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | {{template "layout/back_nav" .}} 29 | {{template "layout/left" .}} 30 | 31 |
32 |
33 |
34 |

{{tsl .Session.Lang "label.preferences"}}

35 |
36 | 37 |
38 | {{template "layout/message" .}} 39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /handler/linkHandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/go-martini/martini" 5 | 6 | . "github.com/easykoo/go-blog/common" 7 | "github.com/easykoo/go-blog/middleware" 8 | "github.com/easykoo/go-blog/model" 9 | 10 | "encoding/json" 11 | ) 12 | 13 | func AllLink(ctx *middleware.Context) { 14 | switch ctx.R.Method { 15 | case "POST": 16 | link := new(model.Link) 17 | link.SetPageActive(true) 18 | link.SetPageSize(ParseInt(ctx.R.FormValue("iDisplayLength"))) 19 | link.SetDisplayStart(ParseInt(ctx.R.FormValue("iDisplayStart"))) 20 | columnNum := ctx.R.FormValue("iSortCol_0") 21 | sortColumn := ctx.R.FormValue("mDataProp_" + columnNum) 22 | link.AddSortProperty(sortColumn, ctx.R.FormValue("sSortDir_0")) 23 | linkArray, total, err := link.SearchByPage() 24 | PanicIf(err) 25 | ctx.Set("aaData", linkArray) 26 | ctx.Set("iTotalDisplayRecords", total) 27 | ctx.Set("iTotalRecords", total) 28 | ctx.JSON(200, ctx.Response) 29 | default: 30 | ctx.HTML(200, "link/allLink", ctx) 31 | } 32 | } 33 | 34 | func InsertLink(ctx *middleware.Context, link model.Link) { 35 | switch ctx.R.Method { 36 | case "POST": 37 | err := link.Insert() 38 | PanicIf(err) 39 | ctx.Set("success", true) 40 | ctx.Set("message", Translate(ctx.S.Get("Lang").(string), "message.send.success")) 41 | ctx.Redirect("/link/all") 42 | default: 43 | ctx.HTML(200, "link/edit", ctx) 44 | } 45 | } 46 | 47 | func DeleteLink(ctx *middleware.Context, params martini.Params) { 48 | id := params["id"] 49 | link := new(model.Link) 50 | link.Id = ParseInt(id) 51 | err := link.Delete() 52 | PanicIf(err) 53 | ctx.Set("success", true) 54 | ctx.JSON(200, ctx.Response) 55 | } 56 | 57 | func DeleteLinkArray(ctx *middleware.Context) { 58 | linkArray := ctx.R.FormValue("linkArray") 59 | link := new(model.Link) 60 | var res []int 61 | json.Unmarshal([]byte(linkArray), &res) 62 | err := link.DeleteLinkArray(res) 63 | PanicIf(err) 64 | ctx.Set("success", true) 65 | ctx.JSON(200, ctx.Response) 66 | } 67 | 68 | func EditLink(ctx *middleware.Context, params martini.Params) { 69 | id := params["id"] 70 | link := new(model.Link) 71 | link.Id = ParseInt(id) 72 | err := link.GetLink() 73 | PanicIf(err) 74 | ctx.Set("Link", link) 75 | 76 | ctx.HTML(200, "link/edit", ctx) 77 | } 78 | -------------------------------------------------------------------------------- /handler/feedbackHandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/go-martini/martini" 5 | 6 | . "github.com/easykoo/go-blog/common" 7 | "github.com/easykoo/go-blog/middleware" 8 | "github.com/easykoo/go-blog/model" 9 | 10 | "encoding/json" 11 | ) 12 | 13 | func FeedbackInfo(ctx *middleware.Context) { 14 | feedback := new(model.Feedback) 15 | feedback.Viewed = false 16 | feedbackArray, count, err := feedback.Info() 17 | PanicIf(err) 18 | ctx.Set("Array", feedbackArray) 19 | ctx.Set("Count", count) 20 | ctx.JSON(200, ctx.Response) 21 | } 22 | 23 | func AllFeedback(ctx *middleware.Context) { 24 | switch ctx.R.Method { 25 | case "POST": 26 | feedback := new(model.Feedback) 27 | feedback.SetPageActive(true) 28 | feedback.SetPageSize(ParseInt(ctx.R.FormValue("iDisplayLength"))) 29 | feedback.SetDisplayStart(ParseInt(ctx.R.FormValue("iDisplayStart"))) 30 | columnNum := ctx.R.FormValue("iSortCol_0") 31 | sortColumn := ctx.R.FormValue("mDataProp_" + columnNum) 32 | feedback.AddSortProperty(sortColumn, ctx.R.FormValue("sSortDir_0")) 33 | feedbackArray, total, err := feedback.SearchByPage() 34 | PanicIf(err) 35 | ctx.Set("aaData", feedbackArray) 36 | ctx.Set("iTotalDisplayRecords", total) 37 | ctx.Set("iTotalRecords", total) 38 | ctx.JSON(200, ctx.Response) 39 | default: 40 | ctx.HTML(200, "feedback/allFeedback", ctx) 41 | } 42 | } 43 | 44 | func DeleteFeedback(ctx *middleware.Context, params martini.Params) { 45 | id := params["id"] 46 | feedback := new(model.Feedback) 47 | feedback.Id = ParseInt(id) 48 | err := feedback.Delete() 49 | PanicIf(err) 50 | ctx.Set("success", true) 51 | ctx.JSON(200, ctx.Response) 52 | } 53 | 54 | func DeleteFeedbackArray(ctx *middleware.Context) { 55 | feedbackArray := ctx.R.FormValue("feedbackArray") 56 | feedback := new(model.Feedback) 57 | var res []int 58 | json.Unmarshal([]byte(feedbackArray), &res) 59 | err := feedback.DeleteFeedbackArray(res) 60 | PanicIf(err) 61 | ctx.Set("success", true) 62 | ctx.JSON(200, ctx.Response) 63 | } 64 | 65 | func ViewFeedback(ctx *middleware.Context, params martini.Params) { 66 | id := params["id"] 67 | feedback := new(model.Feedback) 68 | feedback.Id = ParseInt(id) 69 | err := feedback.SetViewed(true) 70 | PanicIf(err) 71 | ctx.Set("success", true) 72 | ctx.JSON(200, ctx.Response) 73 | } 74 | -------------------------------------------------------------------------------- /templates/layout/blog_side.html: -------------------------------------------------------------------------------- 1 | {{$session := .Session}} 2 | {{$lang := .Session.Lang}} 3 |
4 | 16 | 17 | {{with .DbUtil.GetHotBlog}} 18 |
19 |

{{tsl $lang "label.hot.blog"}}

20 |
21 | {{range .}} 22 |
23 | 27 |
28 | {{end}} 29 |
30 | {{end}} 31 | 32 | {{with .DbUtil.GetRecentComments}} 33 |
34 |

{{tsl $lang "label.latest.comments"}}

35 |
36 | {{range .}} 37 | 49 | {{end}} 50 |
51 | {{end}} 52 | 53 | {{with .Response.Tags}} 54 |
55 |

{{tsl $lang "label.tags"}}

56 |
57 |
58 |
59 | {{range .}} 60 | {{.Name}} 61 | {{end}} 62 |
63 |
64 |
65 | {{end}} 66 |
-------------------------------------------------------------------------------- /public/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv prev3.7.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=s.elements;return"string"==typeof a?a.split(" "):a}function e(a){var b=r[a[p]];return b||(b={},q++,a[p]=q,r[q]=b),b}function f(a,c,d){if(c||(c=b),k)return c.createElement(a);d||(d=e(c));var f;return f=d.cache[a]?d.cache[a].cloneNode():o.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!f.canHaveChildren||n.test(a)||f.tagUrn?f:d.frag.appendChild(f)}function g(a,c){if(a||(a=b),k)return a.createDocumentFragment();c=c||e(a);for(var f=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)f.createElement(h[g]);return f}function h(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return s.shivMethods?f(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(s,b.frag)}function i(a){a||(a=b);var d=e(a);return!s.shivCSS||j||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),k||h(a,d),a}var j,k,l="3.7.0",m=a.html5||{},n=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,o=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,p="_html5shiv",q=0,r={};!function(){try{var a=b.createElement("a");a.innerHTML="",j="hidden"in a,k=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){j=!0,k=!0}}();var s={elements:m.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:l,shivCSS:m.shivCSS!==!1,supportsUnknownElements:k,shivMethods:m.shivMethods!==!1,type:"default",shivDocument:i,createElement:f,createDocumentFragment:g};a.html5=s,i(b)}(this,document); -------------------------------------------------------------------------------- /model/page.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | . "github.com/easykoo/go-blog/common" 5 | ) 6 | 7 | type SortProperty struct { 8 | Column string 9 | Direction string 10 | } 11 | 12 | type Page struct { 13 | pageActive bool 14 | displayStart int 15 | pageNo int 16 | pageSize int 17 | totalRecord int 18 | totalPage int 19 | sortProperties []*SortProperty 20 | Result interface{} 21 | } 22 | 23 | func (self *Page) GetDisplayStart() int { 24 | return self.displayStart 25 | } 26 | 27 | func (self *Page) GetPageNo() int { 28 | return self.pageNo 29 | } 30 | 31 | func (self *Page) GetPreviousPageNo() int { 32 | if self.pageNo <= 1 { 33 | return 1 34 | } 35 | return self.pageNo - 1 36 | } 37 | 38 | func (self *Page) GetNextPageNo() int { 39 | if self.pageNo >= self.totalPage { 40 | return self.totalPage 41 | } 42 | return self.pageNo + 1 43 | } 44 | 45 | func (self *Page) GetPageSize() int { 46 | return self.pageSize 47 | } 48 | 49 | func (self *Page) GetTotalRecord() int { 50 | return self.totalRecord 51 | } 52 | 53 | func (self *Page) GetTotalPage() int { 54 | return self.totalPage 55 | } 56 | 57 | func (self *Page) GetSortProperties() []*SortProperty { 58 | return self.sortProperties 59 | } 60 | 61 | func (self *Page) SetPageActive(active bool) { 62 | self.pageActive = active 63 | } 64 | 65 | func (self *Page) SetPageSize(pageSize int) { 66 | self.pageSize = pageSize 67 | } 68 | 69 | func (self *Page) SetDisplayStart(displayStart int) { 70 | self.initIf() 71 | if ((displayStart + 1) / self.pageSize) < 1 { 72 | self.pageNo = 1 73 | } else { 74 | self.pageNo = ((displayStart + 1) / self.pageSize) + 1 75 | } 76 | self.displayStart = displayStart 77 | } 78 | 79 | func (self *Page) SetPageNo(pageNo int) { 80 | if pageNo < 1 { 81 | pageNo = 1 82 | } 83 | self.pageNo = pageNo 84 | self.displayStart = (pageNo - 1) * self.pageSize 85 | } 86 | 87 | func (self *Page) SetTotalRecord(totalRecord int) { 88 | self.initIf() 89 | self.totalRecord = totalRecord 90 | var totalPage int 91 | if totalRecord%self.pageSize == 0 { 92 | totalPage = totalRecord / self.pageSize 93 | } else { 94 | totalPage = totalRecord/self.pageSize + 1 95 | } 96 | self.totalPage = totalPage 97 | if self.pageNo > totalPage { 98 | self.pageNo = totalPage 99 | } 100 | } 101 | 102 | func (self *Page) AddSortProperty(column string, direction string) { 103 | self.sortProperties = append(self.sortProperties, &SortProperty{Column: Atoa(column), Direction: direction}) 104 | } 105 | 106 | func (self *Page) initIf() { 107 | if self.pageNo == 0 { 108 | self.pageNo = 1 109 | } 110 | if self.pageSize == 0 { 111 | self.pageSize = 10 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /public/codemirror/formatting.min.js: -------------------------------------------------------------------------------- 1 | (function(){function e(e){for(var t=[/for\s*?\((.*?)\)/,/\"(.*?)(\"|$)/,/\'(.*?)(\'|$)/,/\/\*(.*?)(\*\/|$)/,/\/\/.*/],n=[],i=0;t.length>i;i++)for(var a=0;e.length>a;){var r=e.substr(a).match(t[i]);if(null==r)break;n.push({start:a+r.index,end:a+r.index+r[0].length}),a+=r.index+Math.max(1,r[0].length)}return n.sort(function(e,t){return e.start-t.start}),n}function t(e,t){return CodeMirror.innerMode(e.getMode(),e.getTokenAt(t).state).mode}function n(e,t,n,i){var a=e.getMode(),r=e.getLine(t);if(null==i&&(i=r.length),!a.innerMode)return[{from:n,to:i,mode:a}];var o=e.getTokenAt({line:t,ch:n}).state,s=CodeMirror.innerMode(a,o).mode,l=[],c=new CodeMirror.StringStream(r);for(c.pos=c.start=n;;){a.token(c,o);var u=CodeMirror.innerMode(a,o).mode;if(u!=s){var d=c.start;"xml"==s.name&&">"==r.charAt(c.pos-1)&&(d=c.pos),l.push({from:n,to:d,mode:s}),n=d,s=u}if(c.pos>=i)break;c.start=c.pos}return i>n&&l.push({from:n,to:i,mode:s}),l}CodeMirror.extendMode("css",{commentStart:"/*",commentEnd:"*/",wordWrapChars:[";","\\{","\\}"],autoFormatLineBreaks:function(e){return e.replace(RegExp("(;|\\{|\\})([^\r\n])","g"),"$1\n$2")}}),CodeMirror.extendMode("javascript",{commentStart:"/*",commentEnd:"*/",wordWrapChars:[";","\\{","\\}"],autoFormatLineBreaks:function(t){var n=0,i=this.jsonMode?function(e){return e.replace(/([,{])/g,"$1\n").replace(/}/g,"\n}")}:function(e){return e.replace(/(;|\{|\})([^\r\n;])/g,"$1\n$2")},a=e(t),r="";if(null!=a){for(var o=0;a.length>o;o++)a[o].start>n&&(r+=i(t.substring(n,a[o].start)),n=a[o].start),n>=a[o].start&&a[o].end>=n&&(r+=t.substring(n,a[o].end),n=a[o].end);t.length>n&&(r+=i(t.substr(n)))}else r=i(t);return r.replace(/^\n*|\n*$/,"")}}),CodeMirror.extendMode("xml",{commentStart:"",wordWrapChars:[">"],autoFormatLineBreaks:function(e){for(var t=e.split("\n"),n=RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"),i=RegExp("<","g"),a=RegExp("(>)([^\r\n])","g"),r=0;t.length>r;r++){var o=t[r].match(n);null!=o&&o.length>3&&(t[r]=o[1]+o[2].replace(i,"\n$&").replace(a,"$1\n$2")+o[3])}return t.join("\n")}}),CodeMirror.defineExtension("commentRange",function(e,n,i){var a=t(this,n),r=this;this.operation(function(){if(e)r.replaceRange(a.commentEnd,i),r.replaceRange(a.commentStart,n),n.line==i.line&&n.ch==i.ch&&r.setCursor(n.line,n.ch+a.commentStart.length);else{var t=r.getRange(n,i),o=t.indexOf(a.commentStart),s=t.lastIndexOf(a.commentEnd);o>-1&&s>-1&&s>o&&(t=t.substr(0,o)+t.substring(o+a.commentStart.length,s)+t.substr(s+a.commentEnd.length)),r.replaceRange(t,n,i)}})}),CodeMirror.defineExtension("autoIndentRange",function(e,t){var n=this;this.operation(function(){for(var i=e.line;t.line>=i;i++)n.indentLine(i,"smart")})}),CodeMirror.defineExtension("autoFormatRange",function(e,t){var i=this;i.operation(function(){for(var a=e.line,r=t.line;r>=a;++a){for(var o={line:a,ch:a==e.line?e.ch:0},s={line:a,ch:a==r?t.ch:null},l=n(i,a,o.ch,s.ch),c="",u=i.getRange(o,s),d=0;l.length>d;++d){var p=l.length>1?u.slice(l[d].from,l[d].to):u;c&&(c+="\n"),c+=l[d].mode.autoFormatLineBreaks?l[d].mode.autoFormatLineBreaks(p):u}if(c!=u){for(var m=0,h=c.indexOf("\n");-1!=h;h=c.indexOf("\n",h+1),++m);i.replaceRange(c,o,s),a+=m,r+=m}}for(var a=e.line+1;r>=a;++a)i.indentLine(a,"smart");i.setSelection(e,i.getCursor(!1))})})})(); -------------------------------------------------------------------------------- /templates/user/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.sign.in"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | {{template "layout/front_nav" .}} 27 | 28 |
29 |
30 |
31 |

{{tsl .Session.Lang "label.please.sign.in"}}

32 | 33 |
34 | {{template "layout/message" .}} 35 |
36 | 39 | {{with .FieldErrors.username}} 40 |
{{.}}
41 | {{end}} 42 |
43 |
44 | 47 | {{with .FieldErrors.password}} 48 |
{{.}}
49 | {{end}} 50 |
51 | 59 |
60 | 63 |
64 |
65 | {{tsl .Session.Lang "label.need.an.account"}}? 66 | {{tsl .Session.Lang "label.sign.up.now"}}. 67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /templates/user/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.sign.up"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | {{template "layout/front_nav" .}} 27 | 28 |
29 |
30 |
31 |

{{tsl .Session.Lang "label.please.sign.up"}}

32 | 33 |
34 | {{template "layout/message" .}} 35 |
36 | 39 | {{with .FieldErrors.username}} 40 |
{{.}}
41 | {{end}} 42 |
43 |
44 | 47 | {{with .FieldErrors.password}} 48 |
{{.}}
49 | {{end}} 50 |
51 |
52 | 55 | {{with .FieldErrors.email}} 56 |
{{.}}
57 | {{end}} 58 |
59 |
60 | 63 |
64 |
65 | {{tsl .Session.Lang "label.already.have.an.account"}}? 66 | {{tsl .Session.Lang "label.sign.in.now"}}. 67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | {{template "layout/front_nav" .}} 27 | 28 |
29 | 30 |
31 |
32 | 33 | 34 |
35 |
36 |

Business Name or Tagline

37 | 38 |

This is a template that is great for small businesses. It doesn't have too much fancy flare to it, but it 39 | makes a great use of the standard Bootstrap core components. Feel free to use this template for any 40 | project you want!

41 | Call to Action! 42 |
43 |
44 | 45 |
46 | 47 |
48 |
49 |
50 | This is a well that is a great spot for a business tagline or phone number for easy access! 51 |
52 |
53 |
54 | 55 |
56 |
57 |

Heading 1

58 | 59 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam sollicitudin auctor quam ac tempor. Cras 60 | a ante sed libero mollis sodales. Praesent fringilla, neque ut ultrices faucibus, dolor eros ultrices 61 | neque, nec bibendum arcu ipsum eget justo.

62 | More Info 63 |
64 |
65 |

Heading 2

66 | 67 |

Phasellus vestibulum sagittis purus laoreet varius. Pellentesque malesuada malesuada mattis. Aliquam sed 68 | porta nisi, eget suscipit dolor. Nam ipsum sapien, rhoncus eu leo eu, ultricies pellentesque tellus.

69 | More Info 70 |
71 |
72 |

Heading 3

73 | 74 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam sollicitudin auctor quam ac tempor. Cras 75 | a ante sed libero mollis sodales. Praesent fringilla, neque ut ultrices faucibus, dolor eros ultrices 76 | neque, nec bibendum arcu ipsum eget justo.

77 | More Info 78 |
79 |
80 | 81 |
82 | {{template "layout/footer" .}} 83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | 217 | #Custom 218 | *.exe 219 | *.iml 220 | .idea 221 | go-blog -------------------------------------------------------------------------------- /public/codemirror/codemirror.min.css: -------------------------------------------------------------------------------- 1 | .CodeMirror{font-family:monospace;height:300px}.CodeMirror-scroll{overflow:auto}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999}.CodeMirror div.CodeMirror-cursor{border-left:1px solid black;z-index:3}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor{width:auto;border:0;background:#7e7;z-index:1}.cm-tab{display:inline-block}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable{color:black}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-property{color:black}.cm-s-default .cm-operator{color:black}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-s-default .cm-error{color:#f00}.cm-invalidchar{color:#f00}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{line-height:1;position:relative;overflow:hidden;background:white;color:black}.CodeMirror-scroll{margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;padding-right:30px;height:100%;outline:0;position:relative;-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-sizer{position:relative}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;padding-bottom:30px;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;-moz-box-sizing:content-box;box-sizing:content-box;padding-bottom:30px;margin-bottom:-32px;display:inline-block;*zoom:1;*display:inline}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-code pre{border-right:30px solid transparent;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.CodeMirror-wrap .CodeMirror-code pre{border-right:0;width:auto}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-wrap .CodeMirror-scroll{overflow-x:hidden}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-measure pre{position:static}.CodeMirror div.CodeMirror-cursor{position:absolute;visibility:hidden;border-right:0;width:0}.CodeMirror-focused div.CodeMirror-cursor{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.CodeMirror span{*vertical-align:text-bottom}@media print{.CodeMirror div.CodeMirror-cursor{visibility:hidden}} -------------------------------------------------------------------------------- /middleware/memory_store.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gorilla/securecookie" 5 | "github.com/gorilla/sessions" 6 | 7 | . "github.com/easykoo/go-blog/common" 8 | 9 | "encoding/base32" 10 | "net/http" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | // MemoryStore ------------------------------------------------------------ 16 | 17 | func NewMemoryStore(age int) *MemoryStore { 18 | memoryStore := &MemoryStore{ 19 | Codecs: securecookie.CodecsFromPairs([]byte("secret")), 20 | Options: &sessions.Options{ 21 | Path: "/", 22 | MaxAge: age, //default 30 minutes 23 | }, 24 | Container: make(map[string]*SessionInfo), 25 | } 26 | go memoryStore.CheckMemorySessions() 27 | return memoryStore 28 | } 29 | 30 | type MemoryStore struct { 31 | Codecs []securecookie.Codec 32 | Options *sessions.Options // default configuration 33 | Container map[string]*SessionInfo 34 | } 35 | 36 | type SessionInfo struct { 37 | S *sessions.Session 38 | T time.Time 39 | } 40 | 41 | // Get returns a session for the given name after adding it to the registry. 42 | // 43 | func (s *MemoryStore) Get(r *http.Request, name string) (*sessions.Session, error) { 44 | Log.Info("MemoryStore Get()") 45 | return sessions.GetRegistry(r).Get(s, name) 46 | } 47 | 48 | // New returns a session for the given name without adding it to the registry. 49 | // 50 | func (s *MemoryStore) New(r *http.Request, name string) (*sessions.Session, error) { 51 | session := sessions.NewSession(s, name) 52 | opts := *s.Options 53 | session.Options = &opts 54 | session.IsNew = true 55 | var err error 56 | if c, errCookie := r.Cookie(name); errCookie == nil { 57 | Log.Info("MemoryStore reading cookie") 58 | err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...) 59 | PanicIf(err) 60 | if err == nil { 61 | Log.Info("MemoryStore read cookie success") 62 | err = s.load(session) 63 | if err == nil { 64 | session.IsNew = false 65 | } 66 | } 67 | } 68 | return session, err 69 | } 70 | 71 | // Save adds a single session to the response. 72 | func (s *MemoryStore) Save(r *http.Request, w http.ResponseWriter, 73 | session *sessions.Session) error { 74 | Log.Info("MemoryStore Save()", session.ID) 75 | if session.ID == "" { 76 | session.ID = strings.TrimRight( 77 | base32.StdEncoding.EncodeToString( 78 | securecookie.GenerateRandomKey(32)), "=") 79 | } 80 | if err := s.save(session); err != nil { 81 | return err 82 | } 83 | encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, 84 | s.Codecs...) 85 | if err != nil { 86 | return err 87 | } 88 | http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options)) 89 | return nil 90 | } 91 | 92 | // save writes encoded session.Values to a file. 93 | func (s *MemoryStore) save(session *sessions.Session) error { 94 | Log.Info("MemoryStore save()") 95 | s.Container[session.ID] = &SessionInfo{S: session, T: time.Now()} 96 | return nil 97 | } 98 | 99 | // load reads a file and decodes its content into session.Values. 100 | func (s *MemoryStore) load(session *sessions.Session) error { 101 | Log.Info("MemoryStore load()") 102 | Log.Info("MemoryStore load session: ", session.ID) 103 | if _, ok := s.Container[session.ID]; ok { 104 | Log.Info("MemoryStore load session OK ") 105 | session = s.Container[session.ID].S 106 | Log.Info("MemoryStore load SignedUser: ", session.Values["SignedUser"]) 107 | } else { 108 | Log.Info("MemoryStore load session failed ") 109 | } 110 | return nil 111 | } 112 | 113 | func (s *MemoryStore) CheckMemorySessions() { 114 | timer := time.NewTicker(30 * time.Second) 115 | for { 116 | select { 117 | case <-timer.C: 118 | go func() { 119 | s.removeMemorySessions() 120 | }() 121 | } 122 | } 123 | 124 | } 125 | 126 | func (s *MemoryStore) removeMemorySessions() { 127 | for sId, sessionInfo := range s.Container { 128 | if (time.Now().Unix() - sessionInfo.T.Unix()) >= int64(s.Options.MaxAge) { 129 | Log.Info(time.Now().Unix() - sessionInfo.T.Unix()) 130 | delete(s.Container, sId) 131 | Log.Info("Removed: ", sId) 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /templates/layout/front_nav.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | 6 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b 0) 79 | } 80 | 81 | func (self *Context) SetFormErrors(err binding.Errors) { 82 | self.FormErr = err 83 | } 84 | 85 | func (self *Context) JoinFormErrors(err binding.Errors) { 86 | self.init() 87 | for key, val := range err.Fields { 88 | if _, exists := self.FormErr.Fields[key]; !exists { 89 | self.FormErr.Fields[key] = val 90 | } 91 | } 92 | for key, val := range err.Overall { 93 | if _, exists := self.FormErr.Overall[key]; !exists { 94 | self.FormErr.Overall[key] = val 95 | } 96 | } 97 | } 98 | 99 | func (self *Context) AddError(err string) { 100 | self.Errors = append(self.Errors, err) 101 | } 102 | 103 | func (self *Context) AddFieldError(field string, err string) { 104 | self.FormErr.Fields[field] = err 105 | } 106 | 107 | func (self *Context) ClearError() { 108 | self.Errors = self.Errors[:0] 109 | 110 | for key, _ := range self.FormErr.Fields { 111 | if _, exists := self.FormErr.Fields[key]; exists { 112 | delete(self.FormErr.Fields, key) 113 | } 114 | } 115 | 116 | for key, _ := range self.FormErr.Overall { 117 | if _, exists := self.FormErr.Overall[key]; exists { 118 | delete(self.FormErr.Overall, key) 119 | } 120 | } 121 | } 122 | 123 | func (self *Context) HasError() bool { 124 | return self.HasCommonError() || self.HasFieldError() || self.HasOverallError() 125 | } 126 | 127 | func (self *Context) HasCommonError() bool { 128 | return (len(self.Errors) > 0) 129 | } 130 | 131 | func (self *Context) HasFieldError() bool { 132 | return (len(self.FormErr.Fields) > 0) 133 | } 134 | 135 | func (self *Context) HasOverallError() bool { 136 | return (len(self.FormErr.Overall) > 0) 137 | } 138 | 139 | func (self *Context) OverallErrors() map[string]string { 140 | return self.FormErr.Overall 141 | } 142 | 143 | func (self *Context) FieldErrors() map[string]string { 144 | return self.FormErr.Fields 145 | } 146 | 147 | func (self *Context) Session() map[interface{}]interface{} { 148 | return self.S.Values() 149 | } 150 | 151 | func InitContext() martini.Handler { 152 | return func(c martini.Context, s sessions.Session, rnd render.Render, r *http.Request, w http.ResponseWriter) { 153 | ctx := &Context{ 154 | Render: rnd, 155 | W: w, 156 | R: r, 157 | C: c, 158 | S: s, 159 | DbUtil: &model.DbUtil{}, 160 | } 161 | 162 | lang := s.Get("Lang") 163 | if lang == nil { 164 | s.Set("Lang", Cfg.MustValue("", "locale", "en")) 165 | } 166 | 167 | s.Set("Settings", model.GetSettings()) 168 | c.Map(ctx) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /middleware/db_store.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gorilla/securecookie" 5 | . "github.com/gorilla/sessions" 6 | 7 | . "github.com/easykoo/go-blog/common" 8 | "github.com/easykoo/go-blog/model" 9 | 10 | "encoding/base32" 11 | "net/http" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | // DbStore ------------------------------------------------------------ 17 | 18 | // NewSessionStore returns a new DbStore. 19 | // 20 | // The path argument is the directory where sessions will be saved. If empty 21 | // it will use os.TempDir(). 22 | // 23 | // See NewCookieStore() for a description of the other parameters. 24 | func NewDbStore(age int) *DbStore { 25 | sessionStore := &DbStore{ 26 | Codecs: securecookie.CodecsFromPairs([]byte("secret")), 27 | Options: &Options{ 28 | Path: "/", 29 | MaxAge: age, //seconds 30 | }, 31 | } 32 | go sessionStore.CheckDbSessions() 33 | return sessionStore 34 | } 35 | 36 | // DbStore stores sessions in the filesystem. 37 | // 38 | // It also serves as a referece for custom stores. 39 | // 40 | // This store is still experimental and not well tested. Feedback is welcome. 41 | type DbStore struct { 42 | Codecs []securecookie.Codec 43 | Options *Options // default configuration 44 | } 45 | 46 | // MaxLength restricts the maximum length of new sessions to l. 47 | // If l is 0 there is no limit to the size of a session, use with caution. 48 | // The default for a new DbStore is 4096. 49 | func (s *DbStore) MaxLength(l int) { 50 | for _, c := range s.Codecs { 51 | if codec, ok := c.(*securecookie.SecureCookie); ok { 52 | codec.MaxLength(l) 53 | } 54 | } 55 | } 56 | 57 | // Get returns a session for the given name after adding it to the registry. 58 | // 59 | // See CookieStore.Get(). 60 | func (s *DbStore) Get(r *http.Request, name string) (*Session, error) { 61 | return GetRegistry(r).Get(s, name) 62 | } 63 | 64 | // New returns a session for the given name without adding it to the registry. 65 | // 66 | // See CookieStore.New(). 67 | func (s *DbStore) New(r *http.Request, name string) (*Session, error) { 68 | session := NewSession(s, name) 69 | opts := *s.Options 70 | session.Options = &opts 71 | session.IsNew = true 72 | var err error 73 | if c, errCookie := r.Cookie(name); errCookie == nil { 74 | err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...) 75 | PanicIf(err) 76 | if err == nil { 77 | err = s.load(session) 78 | if err == nil { 79 | session.IsNew = false 80 | } 81 | } 82 | } 83 | return session, err 84 | } 85 | 86 | // Save adds a single session to the response. 87 | func (s *DbStore) Save(r *http.Request, w http.ResponseWriter, 88 | session *Session) error { 89 | if session.ID == "" { 90 | session.ID = strings.TrimRight( 91 | base32.StdEncoding.EncodeToString( 92 | securecookie.GenerateRandomKey(32)), "=") 93 | } 94 | if err := s.save(session); err != nil { 95 | return err 96 | } 97 | encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, 98 | s.Codecs...) 99 | if err != nil { 100 | return err 101 | } 102 | http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options)) 103 | return nil 104 | } 105 | 106 | // save writes encoded session.Values to db. 107 | func (s *DbStore) save(session *Session) error { 108 | encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, 109 | s.Codecs...) 110 | if err != nil { 111 | return err 112 | } 113 | sessionInfo := &model.SessionInfo{Id: session.ID, Content: encoded, Age: session.Options.MaxAge} 114 | if (&model.SessionInfo{Id: session.ID}).Exist() { 115 | sessionInfo.Update() 116 | } else { 117 | sessionInfo.Insert() 118 | } 119 | return nil 120 | } 121 | 122 | // load db and decodes its content into session.Values. 123 | func (s *DbStore) load(session *Session) error { 124 | sessionInfo := (&model.SessionInfo{Id: session.ID}).GetSessionInfo() 125 | if err := securecookie.DecodeMulti(session.Name(), sessionInfo.Content, 126 | &session.Values, s.Codecs...); err != nil { 127 | return err 128 | } 129 | return nil 130 | } 131 | 132 | func (s *DbStore) CheckDbSessions() { 133 | timer := time.NewTicker(1 * time.Minute) 134 | for { 135 | select { 136 | case <-timer.C: 137 | go func() { 138 | s.removeDbSessions() 139 | }() 140 | } 141 | } 142 | 143 | } 144 | 145 | func (s *DbStore) removeDbSessions() { 146 | err := (&model.SessionInfo{}).RemoveExpiredSession() 147 | PanicIf(err) 148 | } 149 | -------------------------------------------------------------------------------- /templates/blog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.blog"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | {{template "layout/front_nav" .}} 27 | 28 |
29 | 30 |
31 | 32 | {{template "layout/blog_side" .}} 33 | 34 |
35 | {{$session := .Session}} 36 | {{range .Response.Blog.Result}} 37 |
38 | 39 | {{.Title}} 40 | 41 | {{if $session.SignedUser}} 42 | {{if equal $session.SignedUser.Id .Author.Id}} 43 |    44 | 47 | {{end}} 48 | {{end}} 49 | 50 |
51 | 52 | {{.Author.FullName}} 53 | 54 |    55 | {{tsl $session.Lang "label.post.on"}} 56 | {{if equal $session.Lang "zh"}} 57 | {{.PublishDate | cnFormatTime}} 58 | {{else}} 59 | {{.PublishDate | formatTime}} 60 | {{end}} 61 |    62 |  {{.GetCommentSize}} 63 |    64 |  {{.Visit}} 65 |    66 | {{with .GetTags}} 67 | 68 | {{range .}} 69 | {{.Name}} 70 | {{end}} 71 | {{end}} 72 | 73 |
74 |
75 | {{.Summary | unescaped}} 76 | 77 | {{tsl $session.Lang "label.read.more"}} 78 | 79 | 80 |
81 |
82 | {{end}} 83 | 84 | 85 |
86 |
87 | 88 |
89 | 90 |
91 | {{template "layout/footer" .}} 92 | 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /public/js/jquery.validate.method.js: -------------------------------------------------------------------------------- 1 | jQuery.validator.addMethod("username", function (value, element) { 2 | var username = /^[a-zA-Z][\w]{3,18}[a-zA-Z0-9]$/; 3 | return this.optional(element) || (username.test(value)); 4 | }, "5-20, begin with a letter, end with a letter or number, include letter, numbers and underscore."); 5 | jQuery.validator.addMethod("myPassword", function (value, element) { 6 | return this.optional(element) || value.length >= 6 && value.length <= 20 && /\d/.test(value) && /[a-z]/i.test(value); 7 | }, "6-20, contain at least one number and one letter."); 8 | jQuery.validator.addMethod("fileType", function (value, element, param) { 9 | var fileType = value.substring(value.lastIndexOf(".") + 1).toLowerCase(); 10 | return this.optional(element) || $.inArray(fileType, param) != -1; 11 | }, $.validator.format("Invalid picture type.")); 12 | jQuery.validator.addMethod("fileSize", function (value, element, param) { 13 | return this.optional(element) || element.files[0].size <= param * 1024; 14 | }, $.validator.format("File size is great then {0}.")); 15 | // 字符验证 16 | jQuery.validator.addMethod("stringCheck", function (value, element) { 17 | return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value); 18 | }, "只能包括中文字、英文字母、数字和下划线"); 19 | // 身份证号码验证 20 | jQuery.validator.addMethod("isIdCardNo", function (value, element) { 21 | return this.optional(element) || isIdCardNo(value); 22 | }, "请正确输入您的身份证号码"); 23 | // 手机号码验证 24 | jQuery.validator.addMethod("isMobile", function (value, element) { 25 | var length = value.length; 26 | var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/; 27 | return this.optional(element) || (length == 11 && mobile.test(value)); 28 | }, "请正确填写您的手机号码"); 29 | // 电话号码验证 30 | jQuery.validator.addMethod("isTel", function (value, element) { 31 | var tel = /^\d{3,4}-?\d{7,9}$/; //电话号码格式010-12345678 32 | return this.optional(element) || (tel.test(value)); 33 | }, "请正确填写您的电话号码"); 34 | // 联系电话(手机/电话皆可)验证 35 | jQuery.validator.addMethod("isPhone", function (value, element) { 36 | var length = value.length; 37 | var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/; 38 | var tel = /^\d{3,4}-?\d{7,9}$/; 39 | return this.optional(element) || (tel.test(value) || mobile.test(value)); 40 | }, "请正确填写您的联系电话"); 41 | // 邮政编码验证 42 | jQuery.validator.addMethod("isZipCode", function (value, element) { 43 | var tel = /^[0-9]{6}$/; 44 | return this.optional(element) || (tel.test(value)); 45 | }, "请正确填写您的邮政编码"); 46 | // 手机号码验证 47 | jQuery.validator.addMethod("mobile", function (value, element) { 48 | var length = value.length; 49 | var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/ 50 | return this.optional(element) || (length == 11 && mobile.test(value)); 51 | }, "手机号码格式错误"); 52 | // 电话号码验证 53 | jQuery.validator.addMethod("phone", function (value, element) { 54 | var tel = /^(0[0-9]{2,3}\-)?([2-9][0-9]{6,7})+(\-[0-9]{1,4})?$/; 55 | return this.optional(element) || (tel.test(value)); 56 | }, "电话号码格式错误"); 57 | // 邮政编码验证 58 | jQuery.validator.addMethod("zipCode", function (value, element) { 59 | var tel = /^[0-9]{6}$/; 60 | return this.optional(element) || (tel.test(value)); 61 | }, "邮政编码格式错误"); 62 | // QQ号码验证 63 | jQuery.validator.addMethod("qq", function (value, element) { 64 | var tel = /^[1-9]\d{4,9}$/; 65 | return this.optional(element) || (tel.test(value)); 66 | }, "qq号码格式错误"); 67 | // IP地址验证 68 | jQuery.validator.addMethod("ip", function (value, element) { 69 | var ip = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; 70 | return this.optional(element) || (ip.test(value) && (RegExp.$1 < 256 && RegExp.$2 < 256 && RegExp.$3 < 256 && RegExp.$4 < 256)); 71 | }, "Ip地址格式错误"); 72 | // 字母和数字的验证 73 | jQuery.validator.addMethod("chrnum", function (value, element) { 74 | var chrnum = /^([a-zA-Z0-9]+)$/; 75 | return this.optional(element) || (chrnum.test(value)); 76 | }, "只能输入数字和字母(字符A-Z, a-z, 0-9)"); 77 | // 中文的验证 78 | jQuery.validator.addMethod("chinese", function (value, element) { 79 | var chinese = /^[\u4e00-\u9fa5]+$/; 80 | return this.optional(element) || (chinese.test(value)); 81 | }, "只能输入中文"); 82 | // 下拉框验证 83 | $.validator.addMethod("selectNone", function (value, element) { 84 | return value == "请选择"; 85 | }, "必须选择一项"); 86 | // 字节长度验证 87 | jQuery.validator.addMethod("byteRangeLength", function (value, element, param) { 88 | var length = value.length; 89 | for (var i = 0; i < value.length; i++) { 90 | if (value.charCodeAt(i) > 127) { 91 | length++; 92 | } 93 | } 94 | return this.optional(element) || (length >= param[0] && length <= param[1]); 95 | }, $.validator.format("请确保输入的值在{0}-{1}个字节之间(一个中文字算2个字节)")); -------------------------------------------------------------------------------- /dbscripts/baseData_zh.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM user; 2 | INSERT INTO user (id, username, password, full_name, gender, qq, tel, postcode, address, email, role_id, dept_id, active, locked, create_user, create_date, update_user, update_date, version) 3 | VALUES (1, 'admin', 'b0baee9d279d34fa1dfd71aadb908c3f', 'Admin', 1, 111111, '11122233344', '123456', '自由大道1号', 4 | 'admin@admin.com', 1, 1, 1, 0, 'SYSTEM', now(), 'SYSTEM', now(), 1); 5 | 6 | DELETE FROM role; 7 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 8 | VALUES (1, '管理员', 'SYSTEM', now(), 'SYSTEM', now()); 9 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 10 | VALUES (2, '经理', 'SYSTEM', now(), 'SYSTEM', now()); 11 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 12 | VALUES (3, '员工', 'SYSTEM', now(), 'SYSTEM', now()); 13 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 14 | VALUES (4, '用户', 'SYSTEM', now(), 'SYSTEM', now()); 15 | 16 | DELETE FROM dept; 17 | INSERT INTO dept (id, description, create_user, create_date, update_user, update_date) 18 | VALUES (1, '默认', 'SYSTEM', now(), 'SYSTEM', now()); 19 | 20 | DELETE FROM module; 21 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 22 | VALUES (1, 'Admin', 'SYSTEM', now(), 'SYSTEM', now()); 23 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 24 | VALUES (2, 'Account', 'SYSTEM', now(), 'SYSTEM', now()); 25 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 26 | VALUES (3, 'Feedback', 'SYSTEM', now(), 'SYSTEM', now()); 27 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 28 | VALUES (4, 'News', 'SYSTEM', now(), 'SYSTEM', now()); 29 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 30 | VALUES (5, 'Product', 'SYSTEM', now(), 'SYSTEM', now()); 31 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 32 | VALUES (6, 'Blog', 'SYSTEM', now(), 'SYSTEM', now()); 33 | 34 | DELETE FROM privilege; 35 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 36 | VALUES (1, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 37 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 38 | VALUES (2, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 39 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 40 | VALUES (2, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 41 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 42 | VALUES (3, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 43 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 44 | VALUES (3, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 45 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 46 | VALUES (3, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 47 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 48 | VALUES (4, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 49 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 50 | VALUES (4, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 51 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 52 | VALUES (4, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 53 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 54 | VALUES (5, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 55 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 56 | VALUES (5, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 57 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 58 | VALUES (6, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 59 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 60 | VALUES (6, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 61 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 62 | VALUES (6, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 63 | 64 | DELETE FROM category; 65 | INSERT INTO category (id, description, parent_id, create_user, create_date, update_user, update_date) 66 | VALUES (1, '默认', 0, 'SYSTEM', now(), 'SYSTEM', now()); 67 | 68 | DELETE FROM settings; 69 | INSERT INTO settings (id, app_name, owner_id, about, create_user, create_date, update_user, update_date) 70 | VALUES (1, 'Easy Go', 1, null, 'SYSTEM', now(), 'SYSTEM', now()); 71 | -------------------------------------------------------------------------------- /templates/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.about.us"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | {{with .Session.SignedUser}} 25 | {{if privilege . 1}} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {{end}} 36 | {{end}} 37 | 38 | 39 | 40 | 41 | 42 | {{template "layout/front_nav" .}} 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 |
{{ with .Session.Settings.About}}{{.|unescaped}}{{end}}
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {{if .Session.SignedUser}} 59 | {{if privilege .Session.SignedUser 1}} 60 | 72 | {{end}} 73 | {{end}} 74 |
75 |
76 | {{template "layout/footer" .}} 77 | 78 |
79 | 80 | 81 | 82 | 83 | 84 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /public/codemirror/xml.min.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("xml",function(e,t){function f(e,t){function n(n){return t.tokenize=n,n(e,t)}var r=e.next();if(r=="<"){if(e.eat("!"))return e.eat("[")?e.match("CDATA[")?n(h("atom","]]>")):null:e.match("--")?n(h("comment","-->")):e.match("DOCTYPE",!0,!0)?(e.eatWhile(/[\w\._\-]/),n(p(1))):null;if(e.eat("?"))return e.eatWhile(/[\w\._\-]/),t.tokenize=h("meta","?>"),"meta";var i=e.eat("/");u="";var s;while(s=e.eat(/[^\s\u00a0=<>\"\'\/?]/))u+=s;return u?(a=i?"closeTag":"openTag",t.tokenize=l,"tag"):"tag error"}if(r=="&"){var o;return e.eat("#")?e.eat("x")?o=e.eatWhile(/[a-fA-F\d]/)&&e.eat(";"):o=e.eatWhile(/[\d]/)&&e.eat(";"):o=e.eatWhile(/[\w\.\-:]/)&&e.eat(";"),o?"atom":"error"}return e.eatWhile(/[^&<]/),null}function l(e,t){var n=e.next();if(n==">"||n=="/"&&e.eat(">"))return t.tokenize=f,a=n==">"?"endTag":"selfcloseTag","tag";if(n=="=")return a="equals",null;if(n=="<"){t.tokenize=f;var r=t.tokenize(e,t);return r?r+" error":"error"}return/[\'\"]/.test(n)?(t.tokenize=c(n),t.stringStartCol=e.column(),t.tokenize(e,t)):(e.eatWhile(/[^\s\u00a0=<>\"\']/),"word")}function c(e){var t=function(t,n){while(!t.eol())if(t.next()==e){n.tokenize=l;break}return"string"};return t.isInAttribute=!0,t}function h(e,t){return function(n,r){while(!n.eol()){if(n.match(t)){r.tokenize=f;break}n.next()}return e}}function p(e){return function(t,n){var r;while((r=t.next())!=null){if(r=="<")return n.tokenize=p(e+1),n.tokenize(t,n);if(r==">"){if(e==1){n.tokenize=f;break}return n.tokenize=p(e-1),n.tokenize(t,n)}}return"meta"}}function g(){for(var e=arguments.length-1;e>=0;e--)d.cc.push(arguments[e])}function y(){return g.apply(null,arguments),!0}function b(e,t){var n=s.doNotIndent.hasOwnProperty(e)||d.context&&d.context.noIndent;d.context={prev:d.context,tagName:e,indent:d.indented,startOfLine:t,noIndent:n}}function w(){d.context&&(d.context=d.context.prev)}function E(e){if(e=="openTag")return d.tagName=u,d.tagStart=v.column(),y(N,S(d.startOfLine));if(e=="closeTag"){var t=!1;return d.context?d.context.tagName!=u&&(s.implicitlyClosed.hasOwnProperty(d.context.tagName.toLowerCase())&&w(),t=!d.context||d.context.tagName!=u):t=!0,t&&(m="error"),y(x(t))}return y()}function S(e){return function(t){var n=d.tagName;return d.tagName=d.tagStart=null,t=="selfcloseTag"||t=="endTag"&&s.autoSelfClosers.hasOwnProperty(n.toLowerCase())?(T(n.toLowerCase()),y()):t=="endTag"?(T(n.toLowerCase()),b(n,e),y()):y()}}function x(e){return function(t){return e&&(m="error"),t=="endTag"?(w(),y()):(m="error",y(arguments.callee))}}function T(e){var t;for(;;){if(!d.context)return;t=d.context.tagName.toLowerCase();if(!s.contextGrabbers.hasOwnProperty(t)||!s.contextGrabbers[t].hasOwnProperty(e))return;w()}}function N(e){return e=="word"?(m="attribute",y(C,N)):e=="endTag"||e=="selfcloseTag"?g():(m="error",y(N))}function C(e){if(e=="equals")return y(k,N);if(!s.allowMissing)m="error";else if(e=="word")return m="attribute",y(C,N);return e=="endTag"||e=="selfcloseTag"?g():y()}function k(e){return e=="string"?y(L):e=="word"&&s.allowUnquoted?(m="string",y()):(m="error",e=="endTag"||e=="selfCloseTag"?g():y())}function L(e){return e=="string"?y(L):g()}var n=e.indentUnit,r=t.multilineTagIndentFactor||1,i=t.multilineTagIndentPastTag||!0,s=t.htmlMode?{autoSelfClosers:{area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},implicitlyClosed:{dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},contextGrabbers:{dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}},doNotIndent:{pre:!0},allowUnquoted:!0,allowMissing:!0}:{autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!1,allowMissing:!1},o=t.alignCDATA,u,a,d,v,m;return{startState:function(){return{tokenize:f,cc:[],indented:0,startOfLine:!0,tagName:null,tagStart:null,context:null}},token:function(e,t){!t.tagName&&e.sol()&&(t.startOfLine=!0,t.indented=e.indentation());if(e.eatSpace())return null;m=a=u=null;var n=t.tokenize(e,t);t.type=a;if((n||a)&&n!="comment"){d=t,v=e;for(;;){var r=t.cc.pop()||E;if(r(a||n))break}}return t.startOfLine=!1,m&&(n=m=="error"?n+" error":m),n},indent:function(e,t,s){var u=e.context;if(e.tokenize.isInAttribute)return e.stringStartCol+1;if(e.tokenize!=l&&e.tokenize!=f||u&&u.noIndent)return s?s.match(/^(\s*)/)[0].length:0;if(e.tagName)return i?e.tagStart+e.tagName.length+2:e.tagStart+n*r;if(o&&/",configuration:t.htmlMode?"html":"xml",helperType:t.htmlMode?"html":"xml"}}),CodeMirror.defineMIME("text/xml","xml"),CodeMirror.defineMIME("application/xml","xml"),CodeMirror.mimeModes.hasOwnProperty("text/html")||CodeMirror.defineMIME("text/html",{name:"xml",htmlMode:!0}); -------------------------------------------------------------------------------- /dbscripts/baseData.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM user; 2 | INSERT INTO user (id, username, password, full_name, gender, qq, tel, postcode, address, email, role_id, dept_id, active, locked, create_user, create_date, update_user, update_date, version) 3 | VALUES (1, 'admin', 'b0baee9d279d34fa1dfd71aadb908c3f', 'Admin', 1, 111111, '11122233344', '123456', '自由大道1号', 4 | 'admin@admin.com', 1, 1, 1, 0, 'SYSTEM', now(), 'SYSTEM', now(), 1); 5 | 6 | DELETE FROM role; 7 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 8 | VALUES (1, 'Admin', 'SYSTEM', now(), 'SYSTEM', now()); 9 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 10 | VALUES (2, 'Manager', 'SYSTEM', now(), 'SYSTEM', now()); 11 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 12 | VALUES (3, 'Employee', 'SYSTEM', now(), 'SYSTEM', now()); 13 | INSERT INTO role (id, description, create_user, create_date, update_user, update_date) 14 | VALUES (4, 'User', 'SYSTEM', now(), 'SYSTEM', now()); 15 | 16 | DELETE FROM dept; 17 | INSERT INTO dept (id, description, create_user, create_date, update_user, update_date) 18 | VALUES (1, 'Default', 'SYSTEM', now(), 'SYSTEM', now()); 19 | 20 | DELETE FROM module; 21 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 22 | VALUES (1, 'Admin', 'SYSTEM', now(), 'SYSTEM', now()); 23 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 24 | VALUES (2, 'Account', 'SYSTEM', now(), 'SYSTEM', now()); 25 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 26 | VALUES (3, 'Feedback', 'SYSTEM', now(), 'SYSTEM', now()); 27 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 28 | VALUES (4, 'News', 'SYSTEM', now(), 'SYSTEM', now()); 29 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 30 | VALUES (5, 'Product', 'SYSTEM', now(), 'SYSTEM', now()); 31 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 32 | VALUES (6, 'Blog', 'SYSTEM', now(), 'SYSTEM', now()); 33 | INSERT INTO module (id, description, create_user, create_date, update_user, update_date) 34 | VALUES (7, 'Link', 'SYSTEM', now(), 'SYSTEM', now()); 35 | 36 | DELETE FROM privilege; 37 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 38 | VALUES (1, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 39 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 40 | VALUES (2, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 41 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 42 | VALUES (2, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 43 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 44 | VALUES (3, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 45 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 46 | VALUES (3, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 47 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 48 | VALUES (3, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 49 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 50 | VALUES (4, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 51 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 52 | VALUES (4, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 53 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 54 | VALUES (4, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 55 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 56 | VALUES (5, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 57 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 58 | VALUES (5, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 59 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 60 | VALUES (6, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 61 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 62 | VALUES (6, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 63 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 64 | VALUES (6, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 65 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 66 | VALUES (7, 1, 1, 'SYSTEM', now(), 'SYSTEM', now()); 67 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 68 | VALUES (7, 2, 1, 'SYSTEM', now(), 'SYSTEM', now()); 69 | INSERT INTO privilege (module_id, role_id, dept_id, create_user, create_date, update_user, update_date) 70 | VALUES (7, 3, 1, 'SYSTEM', now(), 'SYSTEM', now()); 71 | 72 | DELETE FROM category; 73 | INSERT INTO category (id, description, parent_id, create_user, create_date, update_user, update_date) 74 | VALUES (1, 'Default', 0, 'SYSTEM', now(), 'SYSTEM', now()); 75 | 76 | DELETE FROM settings; 77 | INSERT INTO settings (id, app_name, owner_id, about, create_user, create_date, update_user, update_date) 78 | VALUES (1, 'Easy Go', 1, null, 'SYSTEM', now(), 'SYSTEM', now()); 79 | -------------------------------------------------------------------------------- /public/js/jquery.easing.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * 4 | * Uses the built in easing capabilities added In jQuery 1.1 5 | * to offer multiple easing options 6 | * 7 | * TERMS OF USE - EASING EQUATIONS 8 | * 9 | * Open source under the BSD License. 10 | * 11 | * Copyright © 2001 Robert Penner 12 | * All rights reserved. 13 | * 14 | * TERMS OF USE - jQuery Easing 15 | * 16 | * Open source under the BSD License. 17 | * 18 | * Copyright © 2008 George McGinley Smith 19 | * All rights reserved. 20 | * 21 | * Redistribution and use in source and binary forms, with or without modification, 22 | * are permitted provided that the following conditions are met: 23 | * 24 | * Redistributions of source code must retain the above copyright notice, this list of 25 | * conditions and the following disclaimer. 26 | * Redistributions in binary form must reproduce the above copyright notice, this list 27 | * of conditions and the following disclaimer in the documentation and/or other materials 28 | * provided with the distribution. 29 | * 30 | * Neither the name of the author nor the names of contributors may be used to endorse 31 | * or promote products derived from this software without specific prior written permission. 32 | * 33 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 34 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 35 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 36 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 37 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 38 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 39 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 40 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 41 | * OF THE POSSIBILITY OF SUCH DAMAGE. 42 | * 43 | */ 44 | jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g= 60*30 { 193 | Log.Info(time.Now().Unix() - f.ModTime().Unix()) 194 | os.Remove(f.Name()) 195 | Log.Info("Removed: ", f.Name()) 196 | } 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /templates/link/edit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.add.link"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | {{template "layout/back_nav" .}} 29 | {{template "layout/left" .}} 30 | 31 |
32 |
33 |
34 |

{{tsl .Session.Lang "label.add.link"}}

35 |
36 | 37 |
38 | {{template "layout/message" .}} 39 |
40 |
41 |
43 | 44 | 45 | 46 |
47 | 51 | 52 |
53 | 54 |
55 |
56 |
57 |
58 | 62 | 63 |
64 | 65 |
66 |
67 |
68 |
69 |
70 | 73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/easykoo/binding" 5 | 6 | . "github.com/easykoo/go-blog/common" 7 | 8 | "net/http" 9 | "regexp" 10 | "strconv" 11 | "time" 12 | ) 13 | 14 | type User struct { 15 | Id int `form:"userId" xorm:"int(11) pk not null autoincr"` 16 | Username string `form:"username" xorm:"varchar(20) not null"` 17 | Password string `form:"password" xorm:"varchar(60) not null"` 18 | FullName string `form:"fullName" xorm:"varchar(20) null"` 19 | Gender int `form:"gender" xorm:"int(1) default 0"` 20 | Qq string `form:"qq" xorm:"varchar(16) default null"` 21 | Tel string `form:"tel" xorm:"varchar(20) null"` 22 | Postcode string `form:"postcode" xorm:"varchar(10) default null"` 23 | Address string `form:"address" xorm:"varchar(80) default null"` 24 | Email string `form:"email" xorm:"varchar(45) unique"` 25 | Role Role `json:"role_id" xorm:"role_id int(3) default 1"` 26 | Dept Dept `json:"dept_id" xorm:"dept_id int(3) default 1"` 27 | Active bool `xorm:"tinyint(1) default 0"` 28 | Locked bool `xorm:"tinyint(1) default 0"` 29 | FailTime int `xorm:"int(1) default 0"` 30 | CreateUser string `xorm:"varchar(20) default 'SYSTEM'"` 31 | CreateDate time.Time `xorm:"datetime created"` 32 | UpdateUser string `xorm:"varchar(20) default 'SYSTEM'"` 33 | UpdateDate time.Time `xorm:"datetime updated"` 34 | Version int `form:"version" xorm:"int(11) version"` 35 | Page `xorm:"-"` 36 | } 37 | 38 | func (self *User) ShowName() string { 39 | if self.FullName != "" { 40 | return self.FullName 41 | } 42 | return self.Username 43 | } 44 | 45 | func (self *User) Exist() (bool, error) { 46 | return orm.Get(self) 47 | } 48 | 49 | func (self *User) ExistUsername() (bool, error) { 50 | return orm.Get(&User{Username: self.Username}) 51 | } 52 | 53 | func (self *User) ExistEmail() (bool, error) { 54 | return orm.Get(&User{Email: self.Email}) 55 | } 56 | 57 | func (self *User) GetUser() (*User, error) { 58 | user := &User{} 59 | _, err := orm.Id(self.Id).Get(user) 60 | return user, err 61 | } 62 | 63 | func (self *User) GetUserById(id int) (*User, error) { 64 | user := &User{Id: id} 65 | _, err := orm.Get(user) 66 | return user, err 67 | } 68 | 69 | func (self *User) Insert() error { 70 | self.FullName = self.Username 71 | self.Dept = Dept{Id: 1} 72 | self.Role = Role{Id: 4} 73 | self.Active = true 74 | self.CreateUser = "SYSTEM" 75 | self.UpdateUser = "SYSTEM" 76 | _, err := orm.InsertOne(self) 77 | Log.Info(self.Username, " inserted") 78 | return err 79 | } 80 | 81 | func (self *User) Update() error { 82 | self.Role = Role{} 83 | self.Dept = Dept{} 84 | _, err := orm.Id(self.Id).MustCols("gender").Update(self) 85 | Log.Info("User ", self.Username, " updated") 86 | return err 87 | } 88 | 89 | func (self *User) Delete() error { 90 | _, err := orm.Delete(self) 91 | Log.Info("User ", self.Username, " deleted") 92 | return err 93 | } 94 | 95 | func (self *User) DeleteUsers(array []int) error { 96 | _, err := orm.In("id", array).Delete(&User{}) 97 | sql := "delete from `user` where id in (" 98 | for index, val := range array { 99 | sql += strconv.Itoa(val) 100 | if index < len(array)-1 { 101 | sql += "," 102 | } 103 | } 104 | sql += ")" 105 | _, err = orm.Exec(sql) 106 | Log.Info("Users: ", array, " deleted") 107 | return err 108 | } 109 | 110 | func (self *User) SetRole() error { 111 | var err error 112 | _, err = orm.Id(self.Id).MustCols("role_id").Update(&User{Role: self.Role, Version: self.Version}) 113 | Log.Info("User ", self.Username, " roleId set to ", self.Role.Id) 114 | return err 115 | } 116 | 117 | func (self *User) SetLock(lock bool) error { 118 | var err error 119 | self, err = self.GetUser() 120 | _, err = orm.Id(self.Id).UseBool("locked").Update(&User{Locked: lock, Version: self.Version}) 121 | if lock { 122 | Log.Info("User ", self.Username, " locked") 123 | } else { 124 | Log.Info("User ", self.Username, " unlocked") 125 | } 126 | return err 127 | } 128 | 129 | func (self *User) SelectAll() ([]User, error) { 130 | var users []User 131 | err := orm.Find(&users) 132 | return users, err 133 | } 134 | 135 | func (self *User) SearchByPage() ([]User, int64, error) { 136 | total, err := orm.Count(self) 137 | var users []User 138 | err = orm.OrderBy(self.GetSortProperties()[0].Column+" "+self.GetSortProperties()[0].Direction).Limit(self.GetPageSize(), self.GetDisplayStart()).Find(&users, self) 139 | return users, total, err 140 | } 141 | 142 | type UserLoginForm struct { 143 | Username string `form:"username" binding:"required"` 144 | Password string `form:"password" binding:"required"` 145 | } 146 | 147 | type UserRegisterForm struct { 148 | Username string `form:"username" binding:"required"` 149 | Password string `form:"password" binding:"required"` 150 | Email string `form:"email" binding:"required"` 151 | } 152 | 153 | type Password struct { 154 | Id int `form:"id" binding:"required"` 155 | CurrentPassword string `form:"currentPassword" binding:"required"` 156 | ConfirmPassword string `form:"confirmPassword" binding:"required"` 157 | } 158 | 159 | func (user UserRegisterForm) Validate(errors *binding.Errors, r *http.Request) { 160 | if len(user.Username) < 5 { 161 | errors.Fields["username"] = "Length of username should be longer than 5." 162 | } 163 | if len(user.Password) < 5 { 164 | errors.Fields["password"] = "Length of password should be longer than 5." 165 | } 166 | re := regexp.MustCompile(".+@.+\\..+") 167 | matched := re.Match([]byte(user.Email)) 168 | if matched == false { 169 | errors.Fields["email"] = "Please enter a valid email address." 170 | } 171 | } 172 | 173 | func (password Password) Validate(errors *binding.Errors, r *http.Request) { 174 | if len(password.ConfirmPassword) < 5 { 175 | errors.Fields["confirmPassword"] = "Length of password should be longer than 5." 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /public/css/dataTables.bootstrap.css: -------------------------------------------------------------------------------- 1 | div.dataTables_length label { 2 | float: left; 3 | text-align: left; 4 | font-weight: normal; 5 | } 6 | 7 | div.dataTables_length select { 8 | width: 75px; 9 | } 10 | 11 | div.dataTables_filter label { 12 | float: right; 13 | font-weight: normal; 14 | } 15 | 16 | div.dataTables_filter input { 17 | width: 16em; 18 | } 19 | 20 | div.dataTables_info { 21 | padding-top: 8px; 22 | } 23 | 24 | div.dataTables_paginate { 25 | float: right; 26 | margin: 0; 27 | } 28 | 29 | div.dataTables_paginate ul.pagination { 30 | margin: 2px 0; 31 | white-space: nowrap; 32 | } 33 | 34 | table.dataTable, 35 | table.dataTable td, 36 | table.dataTable th { 37 | -webkit-box-sizing: content-box; 38 | -moz-box-sizing: content-box; 39 | box-sizing: content-box; 40 | } 41 | 42 | table.dataTable { 43 | clear: both; 44 | margin-top: 6px !important; 45 | margin-bottom: 6px !important; 46 | max-width: none !important; 47 | } 48 | 49 | table.dataTable thead .sorting, 50 | table.dataTable thead .sorting_asc, 51 | table.dataTable thead .sorting_desc, 52 | table.dataTable thead .sorting_asc_disabled, 53 | table.dataTable thead .sorting_desc_disabled { 54 | cursor: pointer; 55 | } 56 | 57 | table.dataTable thead .sorting { 58 | background: url('../images/sort_both.png') no-repeat center right; 59 | } 60 | 61 | table.dataTable thead .sorting_asc { 62 | background: url('../images/sort_asc.png') no-repeat center right; 63 | } 64 | 65 | table.dataTable thead .sorting_desc { 66 | background: url('../images/sort_desc.png') no-repeat center right; 67 | } 68 | 69 | table.dataTable thead .sorting_asc_disabled { 70 | background: url('../images/sort_asc_disabled.png') no-repeat center right; 71 | } 72 | 73 | table.dataTable thead .sorting_desc_disabled { 74 | background: url('../images/sort_desc_disabled.png') no-repeat center right; 75 | } 76 | 77 | table.dataTable th:active { 78 | outline: none; 79 | } 80 | 81 | /* Scrolling */ 82 | 83 | div.dataTables_scrollHead table { 84 | margin-bottom: 0 !important; 85 | border-bottom-left-radius: 0; 86 | border-bottom-right-radius: 0; 87 | } 88 | 89 | div.dataTables_scrollHead table thead tr:last-child th:first-child, 90 | div.dataTables_scrollHead table thead tr:last-child td:first-child { 91 | border-bottom-left-radius: 0 !important; 92 | border-bottom-right-radius: 0 !important; 93 | } 94 | 95 | div.dataTables_scrollBody table { 96 | margin-top: 0 !important; 97 | margin-bottom: 0 !important; 98 | border-top: none; 99 | } 100 | 101 | div.dataTables_scrollBody tbody tr:first-child th, 102 | div.dataTables_scrollBody tbody tr:first-child td { 103 | border-top: none; 104 | } 105 | 106 | div.dataTables_scrollFoot table { 107 | margin-top: 0 !important; 108 | border-top: none; 109 | } 110 | 111 | /* 112 | * TableTools styles 113 | */ 114 | 115 | .table tbody tr.active td, 116 | .table tbody tr.active th { 117 | color: white; 118 | background-color: #08C; 119 | } 120 | 121 | .table tbody tr.active:hover td, 122 | .table tbody tr.active:hover th { 123 | background-color: #0075b0 !important; 124 | } 125 | 126 | .table tbody tr.active a { 127 | color: white; 128 | } 129 | 130 | .table-striped tbody tr.active:nth-child(odd) td, 131 | .table-striped tbody tr.active:nth-child(odd) th { 132 | background-color: #017ebc; 133 | } 134 | 135 | table.DTTT_selectable tbody tr { 136 | cursor: pointer; 137 | } 138 | 139 | div.DTTT .btn { 140 | font-size: 12px; 141 | color: #333 !important; 142 | } 143 | 144 | div.DTTT .btn:hover { 145 | text-decoration: none !important; 146 | } 147 | 148 | ul.DTTT_dropdown.dropdown-menu { 149 | z-index: 2003; 150 | } 151 | 152 | ul.DTTT_dropdown.dropdown-menu a { 153 | color: #333 !important; /* needed only when demo_page.css is included */ 154 | } 155 | 156 | ul.DTTT_dropdown.dropdown-menu li { 157 | position: relative; 158 | } 159 | 160 | ul.DTTT_dropdown.dropdown-menu li:hover a { 161 | color: white !important; 162 | background-color: #0088cc; 163 | } 164 | 165 | div.DTTT_collection_background { 166 | z-index: 2002; 167 | } 168 | 169 | /* TableTools information display */ 170 | 171 | div.DTTT_print_info.modal { 172 | height: 150px; 173 | margin-top: -75px; 174 | text-align: center; 175 | } 176 | 177 | div.DTTT_print_info h6 { 178 | margin: 1em; 179 | font-size: 28px; 180 | font-weight: normal; 181 | line-height: 28px; 182 | } 183 | 184 | div.DTTT_print_info p { 185 | font-size: 14px; 186 | line-height: 20px; 187 | } 188 | 189 | /* 190 | * FixedColumns styles 191 | */ 192 | 193 | div.DTFC_LeftHeadWrapper table, 194 | div.DTFC_LeftFootWrapper table, 195 | div.DTFC_RightHeadWrapper table, 196 | div.DTFC_RightFootWrapper table, 197 | table.DTFC_Cloned tr.even { 198 | background-color: white; 199 | } 200 | 201 | div.DTFC_RightHeadWrapper table, 202 | div.DTFC_LeftHeadWrapper table { 203 | margin-bottom: 0 !important; 204 | border-top-right-radius: 0 !important; 205 | border-bottom-left-radius: 0 !important; 206 | border-bottom-right-radius: 0 !important; 207 | } 208 | 209 | div.DTFC_RightHeadWrapper table thead tr:last-child th:first-child, 210 | div.DTFC_RightHeadWrapper table thead tr:last-child td:first-child, 211 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, 212 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { 213 | border-bottom-left-radius: 0 !important; 214 | border-bottom-right-radius: 0 !important; 215 | } 216 | 217 | div.DTFC_RightBodyWrapper table, 218 | div.DTFC_LeftBodyWrapper table { 219 | margin-bottom: 0 !important; 220 | border-top: none; 221 | } 222 | 223 | div.DTFC_RightBodyWrapper tbody tr:first-child th, 224 | div.DTFC_RightBodyWrapper tbody tr:first-child td, 225 | div.DTFC_LeftBodyWrapper tbody tr:first-child th, 226 | div.DTFC_LeftBodyWrapper tbody tr:first-child td { 227 | border-top: none; 228 | } 229 | 230 | div.DTFC_RightFootWrapper table, 231 | div.DTFC_LeftFootWrapper table { 232 | border-top: none; 233 | } -------------------------------------------------------------------------------- /public/js/easykoo.js: -------------------------------------------------------------------------------- 1 | String.prototype.endWith = function (s) { 2 | if (s == null || s == "" || this.length == 0 || s.length > this.length) 3 | return false; 4 | if (this.substring(this.length - s.length) == s) 5 | return true; 6 | else 7 | return false; 8 | return true; 9 | }; 10 | 11 | var formatTime = function (timeString) { 12 | var date = timeString.substr(0, 10) 13 | var time = timeString.substr(11, 8) 14 | return date + " " + time; 15 | }; 16 | 17 | var changeLanguage = function (lang) { 18 | $.ajax('/language/change/' + lang, { 19 | dataType: 'json', 20 | type: "GET", 21 | success: function (data) { 22 | if (data.success) { 23 | location.reload(); 24 | } 25 | } 26 | }); 27 | } 28 | 29 | var filterSqlStr = function (value) { 30 | var sqlStr = sql_str().split(','); 31 | var flag = false; 32 | 33 | for (var i = 0; i < sqlStr.length; i++) { 34 | if (value.toLowerCase().indexOf(sqlStr[i]) != -1) { 35 | flag = true; 36 | break; 37 | } 38 | } 39 | return flag; 40 | } 41 | 42 | var sql_str = function () { 43 | var str = "and,delete,or,exec,insert,select,union,update,count,*,',join,>,<"; 44 | return str; 45 | } 46 | 47 | var cutoff = function (content) { 48 | var cutoffLine = "----------" 49 | var index = content.indexOf(cutoffLine); 50 | if (index > 0) { 51 | content = content.replace(cutoffLine, ""); 52 | var pre = content.substr(0, index); 53 | var preIndex = pre.lastIndexOf('

'); 54 | if (preIndex > 0) { 55 | preIndex += 4; 56 | } else { 57 | preIndex = pre.lastIndexOf(''); 58 | if (preIndex > 0) { 59 | preIndex += 4; 60 | } else { 61 | return content; 62 | } 63 | } 64 | var pre = content.substr(0, preIndex); 65 | var nex = content.substr(preIndex, content.length); 66 | return pre + cutoffLine + nex; 67 | } 68 | return content; 69 | } 70 | 71 | var direct = function () { 72 | var winHeight = $(window).height() 73 | 74 | var $top = $('#goTop'); 75 | var $bottom = $('#goBottom'); 76 | var side = $('#side').offset().left; 77 | var width = $('#side').width(); 78 | var pos = side + width + 25; 79 | $top.css({ 80 | "left": pos + "px", 81 | "top": winHeight / 2 - 23 + "px", 82 | "width": "45px", 83 | "height": "45px", 84 | "position": "fixed", 85 | "opacity": .4 86 | }) 87 | $bottom.css({ 88 | "left": pos + "px", 89 | "top": winHeight / 2 + 23 + "px", 90 | "width": "45px", 91 | "height": "45px", 92 | "position": "fixed", 93 | "opacity": .4 94 | }) 95 | $(window).scroll(function () { 96 | var scroll = $(this).scrollTop() 97 | if (scroll > 0) { 98 | $top.removeClass("hidden"); 99 | } else { 100 | $top.addClass('hidden'); 101 | } 102 | 103 | if (scroll + winHeight == $(document).height()) { 104 | $bottom.addClass('hidden'); 105 | } else { 106 | $bottom.removeClass("hidden"); 107 | } 108 | }); 109 | $top.on("click", function () { 110 | $('html, body').animate({scrollTop: 0}, 300); 111 | return false; 112 | }) 113 | $bottom.click(function () { 114 | $('html, body').animate({scrollTop: $(document).height()}, 300); 115 | return false; 116 | }); 117 | } 118 | 119 | var setupPage = function (section, pageNo, totalPage) { 120 | var html = ""; 121 | if (totalPage > 1) { 122 | html += ''; 157 | section.html(html); 158 | } 159 | } 160 | 161 | var initTag = function () { 162 | $(".tag").hover(function () { 163 | $(this).find(".badge").removeClass("hidden"); 164 | $(this).parent().css("padding-right", "8px"); 165 | }, function () { 166 | $(this).find(".badge").addClass("hidden"); 167 | $(this).parent().css("padding-right", "15px"); 168 | }) 169 | } -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/easykoo/binding" 5 | "github.com/easykoo/sessions" 6 | "github.com/go-martini/martini" 7 | "github.com/martini-contrib/render" 8 | "github.com/russross/blackfriday" 9 | 10 | . "github.com/easykoo/go-blog/auth" 11 | . "github.com/easykoo/go-blog/common" 12 | "github.com/easykoo/go-blog/handler" 13 | "github.com/easykoo/go-blog/middleware" 14 | "github.com/easykoo/go-blog/model" 15 | 16 | "encoding/gob" 17 | "html/template" 18 | "os" 19 | "time" 20 | ) 21 | 22 | func init() { 23 | SetConfig() 24 | SetLog() 25 | gob.Register(model.User{}) 26 | gob.Register(model.Settings{}) 27 | Log.Debug("server initializing...") 28 | } 29 | 30 | func newMartini() *martini.ClassicMartini { 31 | r := martini.NewRouter() 32 | m := martini.New() 33 | m.Use(middleware.GetLogger()) 34 | m.Map(model.SetEngine()) 35 | m.Use(martini.Recovery()) 36 | m.Use(martini.Static("public")) 37 | m.MapTo(r, (*martini.Routes)(nil)) 38 | m.Action(r.Handle) 39 | 40 | m.Use(sessions.Sessions("my_session", middleware.NewDbStore(7*24*60*60))) 41 | 42 | m.Use(render.Renderer(render.Options{ 43 | Directory: "templates", 44 | Extensions: []string{".tmpl", ".html"}, 45 | Charset: "UTF-8", 46 | Funcs: []template.FuncMap{ 47 | { 48 | "formatTime": func(args ...interface{}) string { 49 | return args[0].(time.Time).Format("Jan _2 15:04") 50 | }, 51 | "cnFormatTime": func(args ...interface{}) string { 52 | return args[0].(time.Time).Format("2006-01-02 15:04") 53 | }, 54 | "mdToHtml": func(args ...interface{}) template.HTML { 55 | return template.HTML(string(blackfriday.MarkdownBasic([]byte(args[0].(string))))) 56 | }, 57 | "unescaped": func(args ...interface{}) template.HTML { 58 | return template.HTML(args[0].(string)) 59 | }, 60 | "equal": func(args ...interface{}) bool { 61 | return args[0] == args[1] 62 | }, 63 | "tsl": func(lang string, format string) string { 64 | return Translate(lang, format) 65 | }, 66 | "tslf": func(lang string, format string, args ...interface{}) string { 67 | return Translatef(lang, format, args...) 68 | }, 69 | "privilege": func(user interface{}, module int) bool { 70 | if user == nil { 71 | return false 72 | } 73 | return CheckPermission(user, module) 74 | }, 75 | "plus": func(args ...int) int { 76 | var result int 77 | for _, val := range args { 78 | result += val 79 | } 80 | return result 81 | }, 82 | }, 83 | }, 84 | })) 85 | 86 | m.Use(middleware.InitContext()) 87 | m.Use(middleware.RecordVisit()) 88 | 89 | return &martini.ClassicMartini{m, r} 90 | } 91 | 92 | func main() { 93 | m := newMartini() 94 | 95 | m.Get("/", handler.Blog) 96 | m.Get("/index", handler.Blog) 97 | m.Get("/about", handler.About) 98 | m.Any("/contact", binding.Form(model.Feedback{}), handler.ContactHandler) 99 | m.Get("/language/change/:lang", handler.LangHandler) 100 | 101 | m.Group("/user", func(r martini.Router) { 102 | r.Any("/all", AuthRequest(Module_Account), handler.AllUserHandler) 103 | r.Any("/logout", handler.LogoutHandler) 104 | r.Any("/login", binding.Form(model.UserLoginForm{}), handler.LoginHandler) 105 | r.Any("/register", binding.Form(model.UserRegisterForm{}), handler.RegisterHandler) 106 | r.Any("/delete", AuthRequest(Module_Account), handler.DeleteUsers) 107 | r.Any("/delete/:id", AuthRequest(Module_Account), handler.DeleteUser) 108 | r.Any("/role", AuthRequest(Module_Account), handler.SetRole) 109 | r.Any("/ban/:id", AuthRequest(Module_Account), handler.BanUser) 110 | r.Any("/lift/:id", AuthRequest(Module_Account), handler.LiftUser) 111 | }) 112 | 113 | m.Group("/profile", func(r martini.Router) { 114 | r.Any("/profile", AuthRequest(SignInRequired), binding.Form(model.User{}), handler.ProfileHandler) 115 | r.Any("/preferences", AuthRequest(SignInRequired), handler.PreferencesHandler) 116 | r.Any("/password", AuthRequest(SignInRequired), binding.Form(model.Password{}), handler.PasswordHandler) 117 | r.Any("/checkEmail", AuthRequest(SignInRequired), binding.Form(model.User{}), handler.CheckEmail) 118 | }) 119 | 120 | m.Group("/admin", func(r martini.Router) { 121 | r.Get("/dashboard", AuthRequest(SignInRequired), handler.DashboardHandler) 122 | r.Any("/settings", AuthRequest(Module_Admin), binding.Form(model.Settings{}), handler.SettingsHandler) 123 | r.Post("/about", AuthRequest(Module_Admin), handler.AboutHandler) 124 | }) 125 | 126 | m.Group("/feedback", func(r martini.Router) { 127 | r.Any("/all", AuthRequest(Module_Feedback), handler.AllFeedback) 128 | r.Any("/info", AuthRequest(Module_Feedback), handler.FeedbackInfo) 129 | r.Any("/delete", AuthRequest(Module_Feedback), handler.DeleteFeedbackArray) 130 | r.Any("/delete/:id", AuthRequest(Module_Feedback), handler.DeleteFeedback) 131 | r.Any("/view/:id", AuthRequest(Module_Feedback), handler.ViewFeedback) 132 | }) 133 | 134 | m.Group("/link", func(r martini.Router) { 135 | r.Any("/all", AuthRequest(Module_Link), handler.AllLink) 136 | r.Any("/insert", AuthRequest(Module_Link), binding.Form(model.Link{}), handler.InsertLink) 137 | r.Any("/delete", AuthRequest(Module_Link), handler.DeleteLinkArray) 138 | r.Any("/delete/:id", AuthRequest(Module_Link), handler.DeleteLink) 139 | r.Any("/edit/:id", AuthRequest(Module_Link), handler.EditLink) 140 | }) 141 | 142 | m.Group("/blog", func(r martini.Router) { 143 | r.Any("", handler.Blog) 144 | r.Any("/tag/:tag", handler.BlogWithTag) 145 | r.Any("/view/:id", handler.ViewBlog) 146 | r.Any("/all", AuthRequest(Module_Blog), handler.AllBlog) 147 | r.Any("/publish", AuthRequest(Module_Blog), binding.Form(model.Blog{}), handler.PublishBlog) 148 | r.Any("/save", AuthRequest(Module_Blog), binding.Form(model.Blog{}), handler.SaveBlog) 149 | r.Any("/edit/:id", AuthRequest(Module_Blog), handler.EditBlog) 150 | r.Any("/delete", AuthRequest(Module_Blog), handler.DeleteBlogArray) 151 | r.Any("/delete/:id", AuthRequest(Module_Blog), handler.DeleteBlog) 152 | r.Any("/forbid/:id", AuthRequest(Module_Blog), handler.ForbidBlog) 153 | r.Any("/permit/:id", AuthRequest(Module_Blog), handler.PermitBlog) 154 | }) 155 | 156 | m.Group("/blog/comment", func(r martini.Router) { 157 | r.Any("", handler.Comment) 158 | r.Any("/delete/:blogId/:seq", AuthRequest(Module_Blog), handler.DeleteComment) 159 | }) 160 | 161 | Log.Info("server is started...") 162 | os.Setenv("PORT", Cfg.MustValue("", "http_port", "3000")) 163 | m.Run() 164 | } 165 | -------------------------------------------------------------------------------- /templates/contact.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.contact.us"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | {{template "layout/front_nav" .}} 27 |
28 |
29 |
30 |
32 |
33 | 36 | 37 |
38 | 39 |
40 |
41 |
42 |
43 | 46 | 47 |
48 | 49 |
50 |
51 |
52 |
53 | 56 | 57 |
58 | 59 |
60 |
61 |
62 |
63 |
64 | 67 |
68 |
69 |
70 |
71 |
72 | 73 |
74 | {{template "layout/footer" .}} 75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /public/css/admin.css: -------------------------------------------------------------------------------- 1 | /* Global Styles */ 2 | 3 | /* ------------------------------- */ 4 | 5 | body { 6 | background-color: #f8f8f8; 7 | font-family: Ubuntu, Consolas, "Microsoft YaHei", Verdana, Tahoma, Arial, sans-serif; 8 | font-size: 14px; 9 | word-wrap:break-word; 10 | } 11 | 12 | /* Wrappers */ 13 | 14 | /* ------------------------------- */ 15 | 16 | #wrapper { 17 | width: 100%; 18 | } 19 | 20 | #page-wrapper { 21 | padding: 0 15px; 22 | min-height: 568px; 23 | background-color: #fff; 24 | } 25 | 26 | @media (min-width: 768px) { 27 | #page-wrapper { 28 | position: inherit; 29 | margin: 0 0 0 250px; 30 | padding: 0 30px; 31 | min-height: 1300px; 32 | border-left: 1px solid #e7e7e7; 33 | } 34 | } 35 | 36 | .navbar-static-side ul li { 37 | border-bottom: 1px solid #e7e7e7; 38 | } 39 | 40 | /* Navigation */ 41 | 42 | /* ------------------------------- */ 43 | 44 | /* Top Right Navigation Dropdown Styles */ 45 | 46 | .navbar-top-links li { 47 | display: inline-block; 48 | } 49 | 50 | .navbar-top-links li:last-child { 51 | margin-right: 15px; 52 | } 53 | 54 | .navbar-top-links li a { 55 | padding: 15px; 56 | min-height: 50px; 57 | } 58 | 59 | .navbar-top-links .dropdown-menu li { 60 | display: block; 61 | } 62 | 63 | .navbar-top-links .dropdown-menu li:last-child { 64 | margin-right: 0; 65 | } 66 | 67 | .navbar-top-links .dropdown-menu li a { 68 | padding: 3px 20px; 69 | min-height: 0; 70 | } 71 | 72 | .navbar-top-links .dropdown-menu li a div { 73 | white-space: normal; 74 | } 75 | 76 | .navbar-top-links .dropdown-messages, 77 | .navbar-top-links .dropdown-tasks, 78 | .navbar-top-links .dropdown-alerts { 79 | width: 310px; 80 | min-width: 0; 81 | } 82 | 83 | .navbar-top-links .dropdown-messages { 84 | right: 0; 85 | left: auto; 86 | } 87 | 88 | .navbar-top-links .dropdown-tasks { 89 | margin-left: -59px; 90 | } 91 | 92 | .navbar-top-links .dropdown-alerts { 93 | margin-left: -123px; 94 | } 95 | 96 | .navbar-top-links .dropdown-user { 97 | right: 0; 98 | left: auto; 99 | } 100 | 101 | /* Sidebar Menu Styles */ 102 | 103 | .sidebar-search { 104 | padding: 15px; 105 | } 106 | 107 | .arrow { 108 | float: right; 109 | } 110 | 111 | .fa.arrow:before { 112 | content: "\f104"; 113 | } 114 | 115 | .active > a > .fa.arrow:before { 116 | content: "\f107"; 117 | } 118 | 119 | .nav-second-level li, 120 | .nav-third-level li { 121 | border-bottom: none !important; 122 | } 123 | 124 | .nav-second-level li a { 125 | padding-left: 37px; 126 | } 127 | 128 | .nav-third-level li a { 129 | padding-left: 52px; 130 | } 131 | 132 | @media (min-width: 768px) { 133 | .navbar-static-side { 134 | z-index: 1; 135 | position: absolute; 136 | width: 250px; 137 | } 138 | 139 | .navbar-top-links .dropdown-messages, 140 | .navbar-top-links .dropdown-tasks, 141 | .navbar-top-links .dropdown-alerts { 142 | margin-left: auto; 143 | } 144 | } 145 | 146 | /* Buttons */ 147 | 148 | /* ------------------------------- */ 149 | 150 | .btn-outline { 151 | color: inherit; 152 | background-color: transparent; 153 | transition: all .5s; 154 | } 155 | 156 | .btn-primary.btn-outline { 157 | color: #428bca; 158 | } 159 | 160 | .btn-success.btn-outline { 161 | color: #5cb85c; 162 | } 163 | 164 | .btn-info.btn-outline { 165 | color: #5bc0de; 166 | } 167 | 168 | .btn-warning.btn-outline { 169 | color: #f0ad4e; 170 | } 171 | 172 | .btn-danger.btn-outline { 173 | color: #d9534f; 174 | } 175 | 176 | .btn-primary.btn-outline:hover, 177 | .btn-success.btn-outline:hover, 178 | .btn-info.btn-outline:hover, 179 | .btn-warning.btn-outline:hover, 180 | .btn-danger.btn-outline:hover { 181 | color: #fff; 182 | } 183 | 184 | /* Pages */ 185 | 186 | /* ------------------------------- */ 187 | 188 | /* Dashboard Chat */ 189 | 190 | .chat { 191 | margin: 0; 192 | padding: 0; 193 | list-style: none; 194 | } 195 | 196 | .chat li { 197 | margin-bottom: 10px; 198 | padding-bottom: 5px; 199 | border-bottom: 1px dotted #B3A9A9; 200 | } 201 | 202 | .chat li.left .chat-body { 203 | margin-left: 60px; 204 | } 205 | 206 | .chat li.right .chat-body { 207 | margin-right: 60px; 208 | } 209 | 210 | .chat li .chat-body p { 211 | margin: 0; 212 | color: #777777; 213 | } 214 | 215 | .panel .slidedown .glyphicon, 216 | .chat .glyphicon { 217 | margin-right: 5px; 218 | } 219 | 220 | .chat-panel .panel-body { 221 | height: 350px; 222 | overflow-y: scroll; 223 | } 224 | 225 | /* Login Page */ 226 | 227 | .login-panel { 228 | margin-top: 25%; 229 | } 230 | 231 | /* Flot Chart Containers */ 232 | 233 | .flot-chart { 234 | display: block; 235 | height: 400px; 236 | } 237 | 238 | .flot-chart-content { 239 | width: 100%; 240 | height: 100%; 241 | } 242 | 243 | /* DataTables Overrides */ 244 | 245 | table.dataTable thead .sorting, 246 | table.dataTable thead .sorting_asc:after, 247 | table.dataTable thead .sorting_desc, 248 | table.dataTable thead .sorting_asc_disabled, 249 | table.dataTable thead .sorting_desc_disabled { 250 | background: transparent; 251 | } 252 | 253 | table.dataTable thead .sorting_asc:after { 254 | content: "\f0de"; 255 | float: right; 256 | font-family: fontawesome; 257 | } 258 | 259 | table.dataTable thead .sorting_desc:after { 260 | content: "\f0dd"; 261 | float: right; 262 | font-family: fontawesome; 263 | } 264 | 265 | table.dataTable thead .sorting:after { 266 | content: "\f0dc"; 267 | float: right; 268 | font-family: fontawesome; 269 | color: rgba(50, 50, 50, .5); 270 | } 271 | 272 | /* Circle Buttons */ 273 | 274 | .btn-circle { 275 | width: 30px; 276 | height: 30px; 277 | padding: 6px 0; 278 | border-radius: 15px; 279 | text-align: center; 280 | font-size: 12px; 281 | line-height: 1.428571429; 282 | } 283 | 284 | .btn-circle.btn-lg { 285 | width: 50px; 286 | height: 50px; 287 | padding: 10px 16px; 288 | border-radius: 25px; 289 | font-size: 18px; 290 | line-height: 1.33; 291 | } 292 | 293 | .btn-circle.btn-xl { 294 | width: 70px; 295 | height: 70px; 296 | padding: 10px 16px; 297 | border-radius: 35px; 298 | font-size: 24px; 299 | line-height: 1.33; 300 | } 301 | 302 | .show-grid [class^="col-"] { 303 | padding-top: 10px; 304 | padding-bottom: 10px; 305 | border: 1px solid #ddd; 306 | background-color: #eee !important; 307 | } 308 | 309 | .show-grid { 310 | margin: 15px 0; 311 | } 312 | 313 | .tag { 314 | margin-top: 5px; 315 | cursor: pointer; 316 | } 317 | 318 | .page { 319 | text-align: center; 320 | } -------------------------------------------------------------------------------- /dbscripts/baseSchema.sql: -------------------------------------------------------------------------------- 1 | DROP SCHEMA IF EXISTS easy_go; 2 | CREATE SCHEMA easy_go 3 | DEFAULT CHARACTER SET utf8 4 | COLLATE utf8_unicode_ci; 5 | USE easy_go; 6 | 7 | DROP TABLE IF EXISTS user; 8 | CREATE TABLE user ( 9 | id INT(11) NOT NULL AUTO_INCREMENT, 10 | username VARCHAR(20) NOT NULL, 11 | password VARCHAR(60) NOT NULL, 12 | full_name VARCHAR(30) DEFAULT NULL, 13 | gender INT(1) DEFAULT '1', 14 | qq VARCHAR(16) DEFAULT NULL, 15 | tel VARCHAR(20) DEFAULT NULL, 16 | postcode VARCHAR(10) DEFAULT NULL, 17 | address VARCHAR(80) DEFAULT NULL, 18 | email VARCHAR(45) DEFAULT NULL, 19 | role_id INT(3) NOT NULL DEFAULT '3', 20 | dept_id INT(3) NOT NULL DEFAULT '1', 21 | active TINYINT(1) DEFAULT '0', 22 | locked TINYINT(1) DEFAULT '0', 23 | fail_time INT(2) DEFAULT '0', 24 | create_user VARCHAR(20) DEFAULT NULL, 25 | create_date DATETIME DEFAULT NULL, 26 | update_user VARCHAR(20) DEFAULT NULL, 27 | update_date DATETIME DEFAULT NULL, 28 | version INT(5) DEFAULT 1, 29 | PRIMARY KEY (id), 30 | UNIQUE KEY username_UNIQUE (username), 31 | UNIQUE KEY email_UNIQUE (email) 32 | ); 33 | 34 | DROP TABLE IF EXISTS role; 35 | CREATE TABLE role ( 36 | id INT(3) NOT NULL AUTO_INCREMENT, 37 | description VARCHAR(20) NOT NULL, 38 | create_user VARCHAR(20) DEFAULT NULL, 39 | create_date DATETIME DEFAULT NULL, 40 | update_user VARCHAR(20) DEFAULT NULL, 41 | update_date DATETIME DEFAULT NULL, 42 | version INT(5) DEFAULT 1, 43 | PRIMARY KEY (id) 44 | ); 45 | 46 | DROP TABLE IF EXISTS dept; 47 | CREATE TABLE dept ( 48 | id INT(3) NOT NULL AUTO_INCREMENT, 49 | description VARCHAR(20) NOT NULL, 50 | create_user VARCHAR(20) DEFAULT NULL, 51 | create_date DATETIME DEFAULT NULL, 52 | update_user VARCHAR(20) DEFAULT NULL, 53 | update_date DATETIME DEFAULT NULL, 54 | version INT(5) DEFAULT 1, 55 | PRIMARY KEY (id) 56 | ); 57 | 58 | DROP TABLE IF EXISTS module; 59 | CREATE TABLE module ( 60 | id INT(3) NOT NULL, 61 | description VARCHAR(40) NOT NULL, 62 | create_user VARCHAR(20) DEFAULT NULL, 63 | create_date DATETIME DEFAULT NULL, 64 | update_user VARCHAR(20) DEFAULT NULL, 65 | update_date DATETIME DEFAULT NULL, 66 | version INT(5) DEFAULT 1, 67 | PRIMARY KEY (id) 68 | ); 69 | 70 | DROP TABLE IF EXISTS privilege; 71 | CREATE TABLE privilege ( 72 | module_id INT(11) NOT NULL, 73 | role_id INT(11) NOT NULL, 74 | dept_id INT(11) NOT NULL, 75 | create_user VARCHAR(20) DEFAULT NULL, 76 | create_date DATETIME DEFAULT NULL, 77 | update_user VARCHAR(20) DEFAULT NULL, 78 | update_date DATETIME DEFAULT NULL, 79 | PRIMARY KEY (module_id, role_id, dept_id) 80 | ); 81 | 82 | DROP TABLE IF EXISTS feedback; 83 | CREATE TABLE feedback ( 84 | id INT(11) NOT NULL AUTO_INCREMENT, 85 | email VARCHAR(45) NOT NULL, 86 | name VARCHAR(20) NOT NULL, 87 | content VARCHAR(200) NOT NULL, 88 | viewed TINYINT(1) DEFAULT '0', 89 | create_date DATETIME DEFAULT NULL, 90 | view_date DATETIME DEFAULT NULL, 91 | PRIMARY KEY (id) 92 | ); 93 | 94 | DROP TABLE IF EXISTS blog; 95 | CREATE TABLE blog ( 96 | id INT(11) NOT NULL AUTO_INCREMENT, 97 | category_id INT(9) NOT NULL DEFAULT 1, 98 | title VARCHAR(60) NOT NULL, 99 | content BLOB NOT NULL, 100 | state VARCHAR(10) NOT NULL, 101 | priority INT(1) NULL DEFAULT 5, 102 | author_id INT(11) DEFAULT NULL, 103 | visit INT(9) DEFAULT 0, 104 | publish_date DATETIME DEFAULT NULL, 105 | forbid_comment TINYINT(1) DEFAULT 0, 106 | create_user VARCHAR(20) DEFAULT NULL, 107 | create_date DATETIME DEFAULT NULL, 108 | update_user VARCHAR(20) DEFAULT NULL, 109 | update_date DATETIME DEFAULT NULL, 110 | version INT(11) DEFAULT 1, 111 | PRIMARY KEY (id) 112 | ); 113 | 114 | DROP TABLE IF EXISTS category; 115 | CREATE TABLE category ( 116 | id INT(3) NOT NULL, 117 | description VARCHAR(40) NOT NULL, 118 | parent_id INT(3) NOT NULL, 119 | create_user VARCHAR(20) DEFAULT NULL, 120 | create_date DATETIME DEFAULT NULL, 121 | update_user VARCHAR(20) DEFAULT NULL, 122 | update_date DATETIME DEFAULT NULL, 123 | version INT(11) DEFAULT 1, 124 | PRIMARY KEY (id) 125 | ); 126 | 127 | DROP TABLE IF EXISTS tag; 128 | CREATE TABLE tag ( 129 | name VARCHAR(20) NOT NULL, 130 | blog_id INT(11) NOT NULL, 131 | PRIMARY KEY (name, blog_id) 132 | ); 133 | 134 | DROP TABLE IF EXISTS settings; 135 | CREATE TABLE settings ( 136 | id INT(1) NOT NULL DEFAULT 1, 137 | app_name VARCHAR(20) NOT NULL, 138 | owner_id INT(11) NOT NULL DEFAULT 1, 139 | about BLOB NULL, 140 | keywords VARCHAR(100) NULL, 141 | description VARCHAR(100) NULL, 142 | create_user VARCHAR(20) DEFAULT NULL, 143 | create_date DATETIME DEFAULT NULL, 144 | update_user VARCHAR(20) DEFAULT NULL, 145 | update_date DATETIME DEFAULT NULL, 146 | version INT(11) DEFAULT 1, 147 | PRIMARY KEY (id) 148 | ); 149 | 150 | DROP TABLE IF EXISTS `comment`; 151 | CREATE TABLE `comment` ( 152 | blog_id INT(11) NOT NULL, 153 | seq INT(5) NOT NULL, 154 | name VARCHAR(20) NULL, 155 | www VARCHAR(45) NULL, 156 | email VARCHAR(45) NULL, 157 | content VARCHAR(150) NOT NULL, 158 | parent_seq INT(3) NULL, 159 | ip VARCHAR(15) NOT NULL, 160 | create_user VARCHAR(20) DEFAULT NULL, 161 | create_date DATETIME DEFAULT NULL, 162 | update_user VARCHAR(20) DEFAULT NULL, 163 | update_date DATETIME DEFAULT NULL, 164 | version INT(11) DEFAULT 1, 165 | PRIMARY KEY (blog_id, seq) 166 | ); 167 | 168 | DROP TABLE IF EXISTS visit; 169 | CREATE TABLE visit ( 170 | session_id VARCHAR(60) NOT NULL, 171 | ip VARCHAR(15) NOT NULL, 172 | user_id INT(11) NULL, 173 | create_date DATETIME NOT NULL, 174 | PRIMARY KEY (session_id) 175 | ); 176 | 177 | DROP TABLE IF EXISTS session_info; 178 | CREATE TABLE session_info ( 179 | id VARCHAR(60) NOT NULL, 180 | content BLOB NOT NULL, 181 | age INT(9) NULL, 182 | create_date DATETIME NOT NULL, 183 | update_date DATETIME DEFAULT NULL, 184 | PRIMARY KEY (id) 185 | ); 186 | 187 | DROP TABLE IF EXISTS link; 188 | CREATE TABLE link ( 189 | id INT(11) NOT NULL AUTO_INCREMENT, 190 | description VARCHAR(40) NOT NULL, 191 | url VARCHAR(80) NOT NULL, 192 | create_user VARCHAR(20) DEFAULT NULL, 193 | create_date DATETIME DEFAULT NULL, 194 | update_user VARCHAR(20) DEFAULT NULL, 195 | update_date DATETIME DEFAULT NULL, 196 | version INT(11) DEFAULT 1, 197 | PRIMARY KEY (id) 198 | ); -------------------------------------------------------------------------------- /templates/profile/password.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.change.password"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | {{template "layout/back_nav" .}} 30 | {{template "layout/left" .}} 31 | 32 |
33 |
34 |
35 |

{{tsl .Session.Lang "label.change.password"}}

36 |
37 | 38 |
39 | {{template "layout/message" .}} 40 |
41 |
42 |
43 | 44 | 45 | 46 | 47 |
48 | 52 | 53 |
54 | 55 |
56 |
57 |
58 |
59 | 63 | 64 |
65 | 66 |
67 |
68 |
69 |
70 | 74 | 75 |
76 | 77 |
78 |
79 |
80 |
81 |
82 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /templates/admin/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{tsl .Session.Lang "label.dashboard"}} - {{.Session.Settings.AppName}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 31 | 32 | 33 | 34 |
35 | {{$session := .Session}} 36 | {{template "layout/back_nav" .}} 37 | {{template "layout/left" .}} 38 |
39 |
40 |
41 |

{{tsl $session.Lang "label.dashboard"}}

42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 | {{tsl .Session.Lang "label.statistics"}} 50 |
51 | 52 |
53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {{with .Response.Visit.GetStatistics}} 66 | 67 | 68 | 69 | 70 | {{end}} 71 | 72 | 73 |
#{{tsl .Session.Lang "label.total.count"}}{{tsl .Session.Lang "label.month.count"}}{{tsl .Session.Lang "label.day.count"}}
#{{.TotalCount}}{{.MonthCount}}{{.DayCount}}
74 |
75 | 76 |
77 | 78 |
79 | 80 |
81 |
82 | 83 |
84 |
85 |
86 |
87 | {{tsl .Session.Lang "label.recent.visit"}} 88 |
89 | 90 |
91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | {{range .Response.Visit.Result}} 105 | 106 | 107 | 108 | 109 | 110 | 118 | 119 | {{end}} 120 | 121 |
#IP{{tsl .Session.Lang "label.user"}}{{tsl .Session.Lang "label.visit.time"}}{{tsl .Session.Lang "label.address"}}
#{{.Ip}}{{.User.FullName}}{{cnFormatTime .CreateDate}} 111 | {{if .Ip}} 112 | 114 | 116 | {{end}} 117 |
122 |
123 | 124 |
125 | 126 |
127 |
128 | 129 |
130 |
131 | 132 |
133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /templates/layout/left.html: -------------------------------------------------------------------------------- 1 | 137 | -------------------------------------------------------------------------------- /handler/userHandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/easykoo/binding" 5 | "github.com/go-martini/martini" 6 | 7 | . "github.com/easykoo/go-blog/common" 8 | "github.com/easykoo/go-blog/middleware" 9 | "github.com/easykoo/go-blog/model" 10 | 11 | "encoding/json" 12 | ) 13 | 14 | func LogoutHandler(ctx *middleware.Context) { 15 | ctx.S.Set("SignedUser", nil) 16 | ctx.Redirect("/index") 17 | } 18 | 19 | func LoginHandler(ctx *middleware.Context, formErr binding.Errors, loginUser model.UserLoginForm) { 20 | switch ctx.R.Method { 21 | case "POST": 22 | ctx.JoinFormErrors(formErr) 23 | password := Md5(loginUser.Password) 24 | user := &model.User{Username: loginUser.Username, Password: password} 25 | if !ctx.HasError() { 26 | if has, err := user.Exist(); has { 27 | PanicIf(err) 28 | if user.Locked { 29 | ctx.Set("User", user) 30 | ctx.AddError(Translate(ctx.S.Get("Lang").(string), "message.error.invalid.username.or.password")) 31 | ctx.HTML(200, "user/login", ctx) 32 | return 33 | } 34 | ctx.S.Set("SignedUser", user) 35 | Log.Info(user.Username, " login") 36 | ctx.Redirect("/admin/dashboard") 37 | } else { 38 | ctx.Set("User", user) 39 | ctx.AddError(Translate(ctx.S.Get("Lang").(string), "message.error.invalid.username.or.password")) 40 | ctx.HTML(200, "user/login", ctx) 41 | } 42 | } else { 43 | ctx.HTML(200, "user/login", ctx) 44 | } 45 | default: 46 | ctx.HTML(200, "user/login", ctx) 47 | } 48 | } 49 | 50 | func RegisterHandler(ctx *middleware.Context, formErr binding.Errors, user model.UserRegisterForm) { 51 | switch ctx.R.Method { 52 | case "POST": 53 | ctx.JoinFormErrors(formErr) 54 | if !ctx.HasError() { 55 | dbUser := model.User{Username: user.Username, Password: user.Password, Email: user.Email} 56 | 57 | if exist, err := dbUser.ExistUsername(); exist { 58 | PanicIf(err) 59 | ctx.AddFieldError("username", Translate(ctx.S.Get("Lang").(string), "message.error.already.exists")) 60 | } 61 | 62 | if exist, err := dbUser.ExistEmail(); exist { 63 | PanicIf(err) 64 | ctx.AddFieldError("email", Translate(ctx.S.Get("Lang").(string), "message.error.already.exists")) 65 | } 66 | 67 | if !ctx.HasError() { 68 | dbUser.Password = Md5(user.Password) 69 | err := dbUser.Insert() 70 | PanicIf(err) 71 | ctx.AddMessage(Translate(ctx.S.Get("Lang").(string), "message.register.success")) 72 | } else { 73 | ctx.Set("User", user) 74 | } 75 | ctx.HTML(200, "user/register", ctx) 76 | } else { 77 | ctx.Set("User", user) 78 | ctx.HTML(200, "user/register", ctx) 79 | } 80 | default: 81 | ctx.HTML(200, "user/register", ctx) 82 | } 83 | } 84 | 85 | func ProfileHandler(ctx *middleware.Context, formErr binding.Errors, user model.User) { 86 | switch ctx.R.Method { 87 | case "POST": 88 | ctx.JoinFormErrors(formErr) 89 | if !ctx.HasError() { 90 | err := user.Update() 91 | PanicIf(err) 92 | dbUser, err := user.GetUserById(user.Id) 93 | PanicIf(err) 94 | ctx.AddMessage(Translate(ctx.S.Get("Lang").(string), "message.change.success")) 95 | ctx.S.Set("SignedUser", dbUser) 96 | } 97 | ctx.HTML(200, "profile/profile", ctx) 98 | default: 99 | ctx.HTML(200, "profile/profile", ctx) 100 | } 101 | } 102 | 103 | func PasswordHandler(ctx *middleware.Context, formErr binding.Errors, password model.Password) { 104 | switch ctx.R.Method { 105 | case "POST": 106 | ctx.JoinFormErrors(formErr) 107 | if !ctx.HasError() { 108 | if password.CurrentPassword == password.ConfirmPassword { 109 | ctx.AddError(Translate(ctx.S.Get("Lang").(string), "message.error.password.not.changed")) 110 | } else { 111 | user := &model.User{Id: password.Id} 112 | dbUser, err := user.GetUserById(user.Id) 113 | PanicIf(err) 114 | if dbUser.Password == Md5(password.CurrentPassword) { 115 | dbUser.Password = Md5(password.ConfirmPassword) 116 | err := dbUser.Update() 117 | PanicIf(err) 118 | ctx.AddMessage(Translate(ctx.S.Get("Lang").(string), "message.change.success")) 119 | } else { 120 | ctx.AddError(Translate(ctx.S.Get("Lang").(string), "message.error.wrong.password")) 121 | } 122 | } 123 | } 124 | default: 125 | } 126 | ctx.HTML(200, "profile/password", ctx) 127 | } 128 | 129 | func CheckEmail(ctx *middleware.Context) { 130 | if user := ctx.S.Get("SignedUser"); user.(model.User).Email != ctx.R.Form["email"][0] { 131 | test := &model.User{Email: ctx.R.Form["email"][0]} 132 | if exist, _ := test.ExistEmail(); exist { 133 | ctx.JSON(200, Translate(ctx.S.Get("Lang").(string), "message.error.already.exists")) 134 | return 135 | } 136 | } 137 | ctx.JSON(200, true) 138 | } 139 | 140 | func AllUserHandler(ctx *middleware.Context) { 141 | switch ctx.R.Method { 142 | case "POST": 143 | user := new(model.User) 144 | user.SetPageActive(true) 145 | user.SetPageSize(ParseInt(ctx.R.FormValue("iDisplayLength"))) 146 | user.SetDisplayStart(ParseInt(ctx.R.FormValue("iDisplayStart"))) 147 | columnNum := ctx.R.FormValue("iSortCol_0") 148 | sortColumn := ctx.R.FormValue("mDataProp_" + columnNum) 149 | user.AddSortProperty(sortColumn, ctx.R.FormValue("sSortDir_0")) 150 | users, total, err := user.SearchByPage() 151 | PanicIf(err) 152 | ctx.Set("aaData", users) 153 | ctx.Set("iTotalDisplayRecords", total) 154 | ctx.Set("iTotalRecords", total) 155 | ctx.JSON(200, ctx.Response) 156 | default: 157 | ctx.HTML(200, "user/allUser", ctx) 158 | } 159 | } 160 | 161 | func DeleteUser(ctx *middleware.Context, params martini.Params) { 162 | id := params["id"] 163 | user := new(model.User) 164 | user.Id = ParseInt(id) 165 | err := user.Delete() 166 | PanicIf(err) 167 | ctx.Set("success", true) 168 | ctx.JSON(200, ctx.Response) 169 | } 170 | 171 | func DeleteUsers(ctx *middleware.Context) { 172 | users := ctx.R.FormValue("Users") 173 | var res []int 174 | json.Unmarshal([]byte(users), &res) 175 | user := new(model.User) 176 | err := user.DeleteUsers(res) 177 | PanicIf(err) 178 | ctx.Set("success", true) 179 | ctx.JSON(200, ctx.Response) 180 | } 181 | 182 | func SetRole(ctx *middleware.Context) { 183 | id := ctx.R.PostFormValue("Id") 184 | roleId := ctx.R.PostFormValue("RoleId") 185 | version := ctx.R.PostFormValue("Version") 186 | user := new(model.User) 187 | user.Id = ParseInt(id) 188 | user.Role.Id = ParseInt(roleId) 189 | user.Version = ParseInt(version) 190 | err := user.SetRole() 191 | PanicIf(err) 192 | ctx.Set("success", true) 193 | Log.Info("User: ", user.Id, " roleId set to ", roleId) 194 | ctx.JSON(200, ctx.Response) 195 | } 196 | 197 | func BanUser(ctx *middleware.Context, params martini.Params) { 198 | id := params["id"] 199 | user := new(model.User) 200 | user.Id = ParseInt(id) 201 | err := user.SetLock(true) 202 | PanicIf(err) 203 | ctx.Set("success", true) 204 | ctx.JSON(200, ctx.Response) 205 | } 206 | 207 | func LiftUser(ctx *middleware.Context, params martini.Params) { 208 | id := params["id"] 209 | user := new(model.User) 210 | user.Id = ParseInt(id) 211 | err := user.SetLock(false) 212 | PanicIf(err) 213 | ctx.Set("success", true) 214 | ctx.JSON(200, ctx.Response) 215 | } 216 | 217 | func PreferencesHandler(ctx *middleware.Context) { 218 | ctx.HTML(200, "profile/preferences", ctx) 219 | } 220 | --------------------------------------------------------------------------------