浏览代码

添加只读用户角色,不能创建、编辑,只能被设置为观察者 (#992)

* 添加只读用户角色,不能创建、编辑,只能被设置为观察者

* Update markdown.preview.css

toc隐藏的时候,article宽度为100%

* Update cherry-markdown.css

cherry样式,隐藏toc时宽度100%

* 发布pdf时候的默认发布者(项目填写了公司名称以公司名称为准)

* 验证只读用户权限
Rendtime 9 月之前
父节点
当前提交
886a412a70

+ 3 - 1
conf/app.conf.example

@@ -22,6 +22,8 @@ highlight_style="${MINDOC_HIGHLIGHT_STYLE||github}"
 #大于0时系统会自动检测配置文件是否变动,变动后自动加载并生效,单位是秒。监听端口和数据库配置无效
 config_auto_delay="${MINDOC_CONFIG_AUTO_DELAY||20}"
 
+#发布pdf时候的默认发布者(项目填写了公司名称以公司名称为准)
+publisher_def =
 
 ########Session储存方式##############
 
@@ -142,7 +144,7 @@ ldap_base="${MINDOC_LDAP_BASE||dc=example,dc=com}"
 ldap_user="${MINDOC_LDAP_USER||cn=ldap helper,ou=example.com,dc=example,dc=com}"
 #第一次绑定ldap用户密码
 ldap_password="${MINDOC_LDAP_PASSWORD||xxx}"
-#自动注册用户角色:0 超级管理员 /1 管理员/ 2 普通用户
+#自动注册用户角色:0 超级管理员 /1 管理员/ 2 普通用户/ 3 只读用户
 ldap_user_role=${MINDOC_LDAP_USER_ROLE||2}
 #ldap搜索filter规则,AD服务器: objectClass=User, openldap服务器: objectClass=posixAccount ,也可以定义为其他属性,如: title=mindoc
 ldap_filter="${MINDOC_LDAP_FILTER||objectClass=posixAccount}"

+ 2 - 0
conf/enumerate.go

@@ -33,6 +33,8 @@ const (
 	MemberAdminRole
 	//普通用户.
 	MemberGeneralRole
+	//只读用户.
+	MemberReaderRole
 )
 
 // 系统角色

+ 2 - 0
conf/lang/en-us.ini

@@ -91,6 +91,7 @@ password_length_invalid = The password cannot be empty and must be between 6-50
 mail_expired = Mail has expired
 captcha_expired = The verification code has expired, please try again.
 user_not_existed = User does not exist
+readusr_only_observer = Read only users can only be set as observers
 email_not_exist = Email does not exist
 failed_save_password = Failed to save password
 mail_service_not_enable = Mail service is not enabled
@@ -498,6 +499,7 @@ status = Status
 super_admin = Super administrator
 admin = Administrator
 user = User
+read_usr = Read-Only User
 normal = Normal
 disable = Disable
 enable = Enable

+ 2 - 0
conf/lang/ru-ru.ini

@@ -91,6 +91,7 @@ password_length_invalid = Пароль не может быть пустым и
 mail_expired = почта просрочена
 captcha_expired = Срок действия капчи истек, попробуйте еще раз
 user_not_existed = этот пользователь не существует
+readusr_only_observer = Толькі для чытання карыстальнікаў можна ўсталяваць толькі як назіральнікі
 email_not_exist = этот адрес электронной почты не существует
 failed_save_password = Не удалось сохранить пароль
 mail_service_not_enable = Служба электронной почты не включена
@@ -496,6 +497,7 @@ status = Статус
 super_admin = Супер администратор
 admin = Администратор
 user = Пользователь
+read_usr = Пользователи только для чтения
 normal = Нормальный
 disable = Отключено
 enable = Включено

+ 2 - 0
conf/lang/zh-cn.ini

@@ -91,6 +91,7 @@ password_length_invalid = 密码不能为空且必须在6-50个字符之间
 mail_expired = 邮件已失效
 captcha_expired = 验证码已过期,请重新操作。
 user_not_existed = 用户不存在
+readusr_only_observer = 只读用户只能设置为观察者
 email_not_exist = 邮箱不存在
 failed_save_password = 保存密码失败
 mail_service_not_enable = 未启用邮件服务
@@ -498,6 +499,7 @@ status = 状态
 super_admin = 超级管理员
 admin = 管理员
 user = 普通用户
+read_usr = 只读用户
 normal = 正常
 disable = 禁用
 enable = 启用

+ 7 - 0
controllers/BlogController.go

@@ -162,6 +162,9 @@ func (c *BlogController) ManageSetting() {
 		bookIdentify := strings.TrimSpace(c.GetString("bookIdentify"))
 		documentId := 0
 
+		if c.Member.Role == conf.MemberReaderRole {
+			c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
+		}
 		if blogTitle == "" {
 			c.JsonResult(6001, i18n.Tr(c.Lang, "message.blog_title_empty"))
 		}
@@ -286,6 +289,10 @@ func (c *BlogController) ManageEdit() {
 	c.Prepare()
 	c.TplName = "blog/manage_edit.tpl"
 
+	if c.Member.Role == conf.MemberReaderRole {
+		c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
+	}
+
 	if c.Ctx.Input.IsPost() {
 		blogId, _ := c.GetInt("blogId", 0)
 

+ 9 - 2
controllers/BookController.go

@@ -466,6 +466,9 @@ func (c *BookController) Create() {
 		editor := c.GetString("editor")
 		itemId, _ := c.GetInt("itemId")
 
+		if c.Member.Role == conf.MemberReaderRole {
+			c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
+		}
 		if bookName == "" {
 			c.JsonResult(6001, i18n.Tr(c.Lang, "message.project_name_empty"))
 		}
@@ -566,7 +569,9 @@ func (c *BookController) Copy() {
 		if _, err := c.IsPermission(); err != nil {
 			c.JsonResult(500, err.Error())
 		}
-
+		if c.Member.Role == conf.MemberReaderRole {
+			c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
+		}
 		identify := strings.TrimSpace(c.GetString("identify", ""))
 		if identify == "" {
 			c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
@@ -587,7 +592,9 @@ func (c *BookController) Copy() {
 
 // 导入zip压缩包或docx
 func (c *BookController) Import() {
-
+	if c.Member.Role == conf.MemberReaderRole {
+		c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
+	}
 	file, moreFile, err := c.GetFile("import-file")
 	if err == http.ErrMissingFile {
 		c.JsonResult(6003, "没有发现需要上传的文件")

+ 8 - 0
controllers/BookMemberController.go

@@ -41,6 +41,10 @@ func (c *BookMemberController) AddMember() {
 	if _, err := models.NewRelationship().FindForRoleId(book.BookId, member.MemberId); err == nil {
 		c.JsonResult(6003, i18n.Tr(c.Lang, "message.user_exist_in_proj"))
 	}
+	//如果是只读用户,只能设置为观察者
+	if member.Role == conf.MemberReaderRole && roleId != int(conf.BookObserver) {
+		c.JsonResult(6003, i18n.Tr(c.Lang, "message.readusr_only_observer"))
+	}
 
 	relationship := models.NewRelationship()
 	relationship.BookId = book.BookId
@@ -94,6 +98,10 @@ func (c *BookMemberController) ChangeRole() {
 	if member.Status == 1 {
 		c.JsonResult(6004, i18n.Tr(c.Lang, "message.user_disable"))
 	}
+	//如果是只读用户,只能设置为观察者
+	if member.Role == conf.MemberReaderRole && role != int(conf.BookObserver) {
+		c.JsonResult(6003, i18n.Tr(c.Lang, "message.readusr_only_observer"))
+	}
 
 	relationship, err := models.NewRelationship().UpdateRoleId(book.BookId, memberId, conf.BookRole(role))
 

+ 4 - 0
controllers/DocumentController.go

@@ -328,6 +328,10 @@ func Flatten(list []*models.DocumentTree, flattened *[]DocumentTreeFlatten) {
 func (c *DocumentController) Edit() {
 	c.Prepare()
 
+	if c.Member.Role == conf.MemberReaderRole {
+		c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
+	}
+
 	identify := c.Ctx.Input.Param(":key")
 	if identify == "" {
 		c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.project_id_error"))

+ 1 - 1
controllers/ManagerController.go

@@ -175,7 +175,7 @@ func (c *ManagerController) ChangeMemberRole() {
 	if memberId <= 0 {
 		c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
 	}
-	if role != int(conf.MemberAdminRole) && role != int(conf.MemberGeneralRole) {
+	if role != int(conf.MemberAdminRole) && role != int(conf.MemberGeneralRole) && role != int(conf.MemberReaderRole) {
 		c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
 	}
 	member := models.NewMember()

+ 4 - 0
models/BookResult.go

@@ -373,8 +373,12 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
 		Toc:          tocList,
 		More:         []string{},
 	}
+
 	if m.Publisher != "" {
 		ebookConfig.Footer = "<p style='color:#8E8E8E;font-size:12px;'>本文档由 <span style='text-decoration:none;color:#1abc9c;font-weight:bold;'>" + m.Publisher + "</span> 生成<span style='float:right'>- _PAGENUM_ -</span></p>"
+	} else if web.AppConfig.DefaultString("publisher_def", "") != "" {
+		defPub := web.AppConfig.DefaultString("publisher_def", "")
+		ebookConfig.Footer = "<p style='color:#8E8E8E;font-size:12px;'>本文档由 <span style='text-decoration:none;color:#1abc9c;font-weight:bold;'>" + defPub + "</span> 生成<span style='float:right'>- _PAGENUM_ -</span></p>"
 	}
 	if m.RealName != "" {
 		ebookConfig.Creator = m.RealName

+ 5 - 3
models/Member.go

@@ -42,8 +42,8 @@ type Member struct {
 	Email       string `orm:"size(100);column(email);unique;description(邮箱)" json:"email"`
 	Phone       string `orm:"size(255);column(phone);null;default(null);description(手机)" json:"phone"`
 	Avatar      string `orm:"size(1000);column(avatar);description(头像)" json:"avatar"`
-	//用户角色:0 超级管理员 /1 管理员/ 2 普通用户 .
-	Role          conf.SystemRole `orm:"column(role);type(int);default(1);index;description(用户角色: 0:超级管理员 1:管理员 2:普通用户)" json:"role"`
+	//用户角色:0 超级管理员 /1 管理员/ 2 普通用户/ 3 只读用户 .
+	Role          conf.SystemRole `orm:"column(role);type(int);default(1);index;description(用户角色: 0:超级管理员 1:管理员 2:普通用户 3:只读用户)" json:"role"`
 	RoleName      string          `orm:"-" json:"role_name"`
 	Status        int             `orm:"column(status);type(int);default(0);description(状态  0:启用 1:禁用)" json:"status"` //用户状态:0 正常/1 禁用
 	CreateTime    time.Time       `orm:"type(datetime);column(create_time);auto_now_add;description(创建时间)" json:"create_time"`
@@ -389,6 +389,8 @@ func (m *Member) ResolveRoleName() {
 		m.RoleName = i18n.Tr(m.Lang, "uc.admin")
 	} else if m.Role == conf.MemberGeneralRole {
 		m.RoleName = i18n.Tr(m.Lang, "uc.user")
+	} else if m.Role == conf.MemberReaderRole {
+		m.RoleName = i18n.Tr(m.Lang, "uc.read_usr")
 	}
 }
 
@@ -473,7 +475,7 @@ func (m *Member) Valid(is_hash_password bool) error {
 	if strings.Count(m.Description, "") > 500 {
 		return ErrMemberDescriptionTooLong
 	}
-	if m.Role != conf.MemberGeneralRole && m.Role != conf.MemberSuperRole && m.Role != conf.MemberAdminRole {
+	if m.Role != conf.MemberGeneralRole && m.Role != conf.MemberSuperRole && m.Role != conf.MemberAdminRole && m.Role != conf.MemberReaderRole {
 		return ErrMemberRoleError
 	}
 	if m.Status != 0 && m.Status != 1 {

+ 1 - 1
models/MemberResult.go

@@ -61,7 +61,7 @@ func (m *MemberRelationshipResult) ResolveRoleName(lang string) *MemberRelations
 	} else if m.RoleId == conf.BookEditor {
 		m.RoleName = i18n.Tr(lang, "common.editor")
 	} else if m.RoleId == conf.BookObserver {
-		m.RoleName = i18n.Tr(lang, "common.obverser")
+		m.RoleName = i18n.Tr(lang, "common.observer")
 	}
 	return m
 }

+ 8 - 0
static/cherry/cherry-markdown.css

@@ -1969,6 +1969,14 @@ div[data-type=codeBlock] .token.inserted {
 .whole-article-wrap > div > .markdown-article {
   width: calc(100% - 260px);
 }
+@media screen and (max-width: 839px) {
+  .toc {
+      display: none !important;
+  }
+  .whole-article-wrap > div > .markdown-article {
+    width: 100%;
+  }
+}
 
 [data-code-block-theme=coy] div[data-type=codeBlock] pre[class*=language-]:after {
   right: 0.75em;

+ 3 - 0
static/css/markdown.preview.css

@@ -159,6 +159,9 @@
     .article-body .markdown-toc{
         display: none;
     }
+    .article-body .markdown-article{
+        width: 100%;
+    }
     .article-body .markdown-article{
         margin-right: 0;
     }

+ 2 - 0
views/blog/manage_list.tpl

@@ -36,7 +36,9 @@
                     <div class="box-head">
                         <strong class="box-title">{{i18n .Lang "blog.blog_list"}}</strong>
                         &nbsp;
+                        {{if eq .Member.Role 0 1 2 }}
                         <a href="{{urlfor "BlogController.ManageSetting"}}" class="btn btn-success btn-sm pull-right">{{i18n .Lang "blog.add_blog"}}</a>
+                        {{end}}
                     </div>
                 </div>
                 <div class="box-body" id="blogList">

+ 2 - 0
views/book/index.tpl

@@ -37,8 +37,10 @@
                     <div class="box-head">
                         <strong class="box-title">{{i18n $.Lang "blog.project_list"}}</strong>
                         &nbsp;
+                        {{if eq .Member.Role 0 1 2 }}
                         <button type="button" data-toggle="modal" data-target="#addBookDialogModal" class="btn btn-success btn-sm pull-right">{{i18n $.Lang "blog.add_project"}}</button>
                         <button type="button" data-toggle="modal" data-target="#importBookDialogModal" class="btn btn-primary btn-sm pull-right" style="margin-right: 5px;">{{i18n $.Lang "blog.import_project"}}</button>
+                        {{end}}
                     </div>
                 </div>
                 <div class="box-body" id="bookList">

+ 1 - 1
views/document/template_api.tpl

@@ -64,7 +64,7 @@
 
 |参数名|类型|说明|
 |:-----  |:-----|-----                           |
-|group_level |int   |用户组id,1:超级管理员;2:普通用户  |
+|group_level |int   |用户组id,1:超级管理员;2:普通用户;3:只读用户  |
 
 #### 备注:
 

+ 1 - 0
views/manager/users.tpl

@@ -77,6 +77,7 @@
                                             <ul class="dropdown-menu">
                                                 <li><a href="javascript:;" @click="setMemberRole(item.member_id,1)">{{i18n $.Lang "uc.admin"}}</a> </li>
                                                 <li><a href="javascript:;" @click="setMemberRole(item.member_id,2)">{{i18n $.Lang "uc.user"}}</a> </li>
+                                                <li><a href="javascript:;" @click="setMemberRole(item.member_id,3)">{{i18n $.Lang "uc.read_usr"}}</a> </li>
                                             </ul>
                                             </div>
                                         </template>