刘光辉
3 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package jnpf.bizcommon.onlyoffice.service.impl;
 
import java.util.LinkedHashMap;
import java.util.Map;
 
/** Shared UI policy: preview and comparison must inherit the same menu defaults. */
final class OnlyOfficeUiConfig {
    private OnlyOfficeUiConfig() { }
 
    static Map<String, Object> previewCustomization() {
        return customization(false, false, true);
    }
 
    static Map<String, Object> previewPermissions() {
        return permissions(false, true, false, false);
    }
 
    /** 签批页禁止用户下载和打印,编辑器仍可在会话关闭时通过回调回写最终文件。 */
    static Map<String, Object> qianpiyePermissions(boolean editable, boolean hideComments) {
        return permissions(editable, hideComments, false, false);
    }
 
    static Map<String, Object> permissions(boolean editable, boolean hideComments,
                                            boolean allowDownload, boolean allowPrint) {
        Map<String, Object> permissions = new LinkedHashMap<>();
        permissions.put("edit", editable);
        // Both permission and customization switches are needed to hide comments.
        if (hideComments) {
            permissions.put("comment", false);
            permissions.put("review", false);
        }
        permissions.put("chat", false);
        permissions.put("download", allowDownload);
        permissions.put("print", allowPrint);
        // This supported permission hides Protection without white-label layout settings.
        permissions.put("protect", false);
        return permissions;
    }
 
    static void applyReviewPermissions(Map<String, Object> permissions) {
        permissions.put("edit", false);
        permissions.put("review", true);
        permissions.put("fillForms", false);
        permissions.put("comment", true);
        permissions.put("reviewGroups", java.util.Collections.emptyList());
        permissions.put("editCommentAuthorOnly", true);
        permissions.put("deleteCommentAuthorOnly", true);
        permissions.put("modifyContentControl", false);
    }
 
    static void applyCommentPreviewPermissions(Map<String, Object> permissions) {
        permissions.put("edit", false);
        permissions.put("review", false);
        // With editorConfig.mode=view this exposes existing comments, never a writable session.
        permissions.put("comment", true);
        permissions.put("fillForms", false);
        permissions.put("modifyContentControl", false);
    }
 
    static void showReviewComments(Map<String, Object> customization, boolean writable) {
        customization.put("comments", true);
        customization.put("hideRightMenu", false);
        Map<String, Object> review = new LinkedHashMap<>();
        review.put("showReviewChanges", true);
        review.put("reviewDisplay", "markup");
        if (writable) review.put("trackChanges", true);
        customization.put("review", review);
    }
 
    /**
     * 编辑器界面裁剪。**必须在后端拼进 config**:customization 在 JWT 签名范围内,
     * 前端往 config 里塞字段会让 DS 验签失败(前端只能追加 events,函数不参与序列化)。
     *
     * <p>两类考虑分开看:
     * <ul>
     *   <li><b>安全</b>:{@code macros}/{@code plugins}。附件是用户上传的外来文档,
     *       可能带宏;插件(含 AI 入口)会把文档内容发往第三方,都不该默认开着。</li>
     *   <li><b>界面噪音</b>:帮助、反馈、顶栏文件名(弹窗标题已经有了)、右侧面板。
     *       这些纯属观感,改动无风险。</li>
     * </ul>
     *
     * <p>⚠️ logo / about / layout 那一类属**白标定制**,社区版镜像大概率不生效,故此处不设——
     * 需要的话得先实测再加,别写了以为生效。
     *
     * @param editable 是否可编辑;forcesave/autosave 只在编辑态有意义,顶栏展开与否也随它变
     */
    static Map<String, Object> customization(boolean editable, boolean pluginsEnabled, boolean hideComments) {
        Map<String, Object> customization = new LinkedHashMap<>();
 
        // ── 安全 ──
        // 外来 docx 可能带宏:不自动运行,且不给用户开的入口
        customization.put("macros", false);
        customization.put("macrosMode", "disable");
        // 仅后端白名单场景开启指定插件;空场景仍关闭全部插件入口。
        customization.put("plugins", pluginsEnabled);
 
        // ── 界面收敛 ──
        // 默认关闭拼写检查;用户在界面修改后,localStorage 仍会覆盖这个初始值。
        Map<String, Object> features = new LinkedHashMap<>();
        features.put("spellcheck", false);
        customization.put("features", features);
        if (hideComments) {
            // 批注面板的另一半开关,与 permissions.comment=false 必须成对出现。
            customization.put("comments", false);
            Map<String, Object> review = new LinkedHashMap<>();
            review.put("trackChanges", false);
            review.put("showReviewChanges", false);
            customization.put("review", review);
        }
        customization.put("help", false);
        customization.put("feedback", false);
        // 左下角那个「提出功能建议」归 suggestFeature 管,不是 feedback(实测得知)
        customization.put("suggestFeature", false);
        // 顶栏不再重复显示文件名——我们的弹窗标题已经有了
        customization.put("toolbarHideFileName", true);
        customization.put("hideRightMenu", true);
        // 顶栏形态按模式分:编辑态展开成完整功能区(工具找得到),只读态收成单行紧凑版
        // (没有可用的编辑按钮,展开只是白占垂直空间)。这与 DS 自己的默认取向一致,
        // 但仍显式写出——默认值随版本变过,别指望省略。
        //
        // ⚠️ 本项会被浏览器 localStorage 覆盖:用户手动折叠过顶栏,之后打开就一直是折叠的,
        // 后端传什么都不算数。验证时请换无痕窗口,别以为配置没生效。
        customization.put("compactToolbar", !editable);
 
        customization.put("autosave", false);
        if (editable) {
            // forcesave:点保存即触发 status 6 回调,不必等全员退出才落盘
            customization.put("forcesave", true);
        }
        return customization;
    }
 
    /**
     * 签批页保留后端签发并自动启动的标注插件,不保存中间版本。
     * Docs 9.4 将 {@code customization.plugins} 同时用作插件运行时总开关;设为 false
     * 会让 autostart 失效,不能用它单独隐藏插件管理器入口。
     */
    static Map<String, Object> qianpiyeCustomization(boolean editable, boolean hideComments) {
        Map<String, Object> customization = customization(editable, true, hideComments);
        customization.put("autosave", false);
        customization.put("forcesave", false);
        return customization;
    }
 
}