1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-04-28 03:10:45 +00:00

Compare commits

...

961 Commits

Author SHA1 Message Date
欢乐马
388a28c00e
Merge pull request #7779 from PPazderski/master
fix typo in seahub scripts
2025-04-28 09:54:06 +08:00
Paul Pazderski
c0e252d53b fix typo in seahub scripts 2025-04-27 18:40:57 +02:00
llj
ea580c6569
[dir view] added 'star/unstar' to the context menu of items in grid mode, and to the dropdown menu in the top toolbar for the single selected item; and other improments (#7776) 2025-04-27 17:51:40 +08:00
Aries
281b469b78
aggregate tags search results (#7775)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-27 13:44:59 +08:00
Michael An
f46da268ff Revert "fix import code bug"
This reverts commit 371dd4a057.
2025-04-27 11:07:32 +08:00
Huang Junxiang
5fc9b4877c
perf: read db & redis conf from .env (#7774)
* perf: read db & redis conf from .env

* perf: remove locmeme cache

* perf: remove db conf in init
2025-04-27 10:49:11 +08:00
llj
2006ff57d6 [share admin] added a missing file 2025-04-27 10:32:32 +08:00
Michael An
371dd4a057 fix import code bug 2025-04-27 09:53:43 +08:00
r350178982
09ea1ce72d Merge branch '12.0' 2025-04-25 18:16:11 +08:00
Ranjiwei
11734fe181
update (#7753)
* update

* code optimize

* Update mail.py
2025-04-25 18:12:27 +08:00
Michael An
52f71ffae1
Merge pull request #7771 from haiwen/feature/create_tag
add create tag btn on tags tree header
2025-04-25 18:11:30 +08:00
llj
66d923ccca
Share admin links batch delete (#7766)
* [Share Admin] Share Links: added 'select links and delete them in batch'

* [Share Admin] Upload Links: added 'select links and delete them in batch'

* [Share Admin] Share / Upload Links: replaced 'Cancel' & 'Delete' buttons with 'xx Selected' button & 'delete' icon

* [Share Admin] Share / Upload Links: added background color for highlighted & selected items
2025-04-25 17:07:15 +08:00
Michael An
3488ea86b0 Merge branch '12.0' 2025-04-25 16:30:34 +08:00
Michael An
29fdd70f99
update translation (#7772) 2025-04-25 16:27:40 +08:00
小强
6549f48998 update sdoc editor version 2025-04-25 14:32:31 +08:00
Michael An
04aec222ce
Merge pull request #7770 from haiwen/remove-svg-sprite-loader
remove svg-sprite-loader to fix npm audit
2025-04-25 13:43:38 +08:00
zhouwenxuan
dda7d350be add create tag btn on tags tree header 2025-04-25 10:38:09 +08:00
Michael An
726e34d47c remove svg-sprite-loader 2025-04-25 10:08:50 +08:00
Michael An
065ecb0b1f
Merge pull request #7768 from haiwen/fix-external-link-page-do-not-show-migrate-tag-info
fix external link page do not show migrate tag info
2025-04-24 21:59:40 +08:00
Michael An
0b00f08e3f fix external link page do not show migrate tag info 2025-04-24 21:53:06 +08:00
欢乐马
b43ad4132b
seafile.sh IS_PRO_VERSION (#7765) 2025-04-24 12:15:57 +08:00
Aries
2ec7bc4da9
Optimize/search filters (#7761)
* optimize ui

* optimize filters

* update date filter

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-23 16:11:03 +08:00
Michael An
75cde31370
Merge pull request #7751 from haiwen/upload-replace-fixup
[dir view] fixed 'upload & replace a file'(so that the related upload…
2025-04-23 15:51:30 +08:00
Michael An
bffdcdfa7e
Merge pull request #7764 from haiwen/fix-svg-package-npm-audit
fix npm audit
2025-04-23 15:21:42 +08:00
Michael An
f2952cb6fb fix npm audit 2025-04-23 15:18:03 +08:00
小强
f10c51c461 fix exdraw bug 2025-04-23 14:58:52 +08:00
awu0403
c6afca8eba
fix link invite group member (#7763)
* fix link invite group member

* Update group-op-menu.js

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-04-23 14:20:44 +08:00
zhichaona
8e2be63d3f
Add excl draw module 4 (#7740)
* add exdraw apis

* temporarily submit

* rebase exdraw_apis

* Interacting with the exdraw-server

* update

* optimize code

* optimize code

---------

Co-authored-by: ‘JoinTyang’ <yangtong1009@163.com>
Co-authored-by: 小强 <shuntian@xiaoqiangdeMacBook-Pro.local>
2025-04-23 14:10:11 +08:00
Michael An
444e690255
Merge pull request #7754 from haiwen/dependabot/npm_and_yarn/frontend/http-proxy-middleware-2.0.9
Bump http-proxy-middleware from 2.0.7 to 2.0.9 in /frontend
2025-04-23 12:07:15 +08:00
Michael An
0cd14ab806
Merge pull request #7759 from haiwen/windows-ui
fixed windows-ui
2025-04-23 11:58:09 +08:00
Ranjiwei
1a4d7038a9
Update lib-sub-folder-set-user-permission-dialog.js (#7762) 2025-04-23 11:43:52 +08:00
llj
bfc2812a4f
[login] 2fa: redesigned 2 pages(2fa with a token; 2fa with a backup code) (#7760) 2025-04-22 21:23:11 +08:00
llj
1ba5c44839
[dir view] the top toolbar for 1 selected dirent: display 'share' icon (remove 'share' from the downdown menu), replaced the 'vertical dots' icon with a 'horizontal dots' icon; other improvements (#7758) 2025-04-22 21:22:29 +08:00
小强
45c9411973 update sdoc translate 2025-04-22 16:29:26 +08:00
zhichaona
36af8862b1 fixed windows-ui 2025-04-22 16:28:37 +08:00
小强
145f2d17db update sdoc editor version 2025-04-22 16:07:01 +08:00
小强
e68a442a3e update sdoc editor version 2025-04-22 16:06:05 +08:00
Ranjiwei
6874c2a40c
Update onlyoffice_file_view_react.html (#7757) 2025-04-22 16:00:18 +08:00
欢乐马
52e020e227
13.0.2 sql (#7756) 2025-04-22 10:49:22 +08:00
dependabot[bot]
d7e4866fc0
Bump http-proxy-middleware from 2.0.7 to 2.0.9 in /frontend
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.7 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-22 01:59:51 +00:00
Michael An
7ff4b52005
basic file support comment (#7731)
* basic file support comment

* 01 add init loading icon

* delete useless comment

* 02 delete comment tip

* update api validation

* 03 update API params

* 04 delete useless api

* 05 remove read all notification

* 06 change comment and reply permission

* 07 change docUuid to fileUuid

---------

Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-04-21 21:33:13 +08:00
Aries
1cf26c3d2c
list repo tags regardless of whether metadata tag is enabled (#7752)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-21 18:25:43 +08:00
llj
84e763cddf [dir view] fixed 'upload & replace a file'(so that the related upload data can be displayed in the popover) 2025-04-21 17:32:27 +08:00
Aries
63f51d6d2a
Feature/search filters controller (#7739)
* add search filter controller

* update custom date

* optimize ui

* update bg color

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-21 15:50:06 +08:00
awu0403
8cc5815107
Send notification to cloud user (#6415)
* send subscription expire notification

* optimize code

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-04-21 13:30:35 +08:00
Michael An
0b8aa00f4d
Merge pull request #7743 from haiwen/fix/dirent_selection
fix dirent state in grid view
2025-04-21 10:42:14 +08:00
Michael An
4c3cc1ae19 fix code format 2025-04-21 10:34:49 +08:00
zhouwenxuan
920b7fe430 fix dirent state in grid view 2025-04-21 10:27:09 +08:00
awu0403
e11dd9e34c
fix special folder judgement (#7749)
Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-04-20 09:18:54 +08:00
awu0403
f24516e88a
Add deactivation option (#7626)
* sql delete share relation

* update rpc remove share

* update

* use sql remove share

* code optimize

* code optimize

* update selector

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-04-20 08:54:41 +08:00
awu0403
38c6ea36ae
Optimize dir size (#7732)
* add dir size

* update

* use sql query file md

* optimize sql

* update sql

* exclude special dir

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-04-20 08:53:12 +08:00
lian
e20902279e set org setting is_active default True 2025-04-19 16:07:07 +08:00
JoinTyang
62f31eee77
fix update records bug (#7747) 2025-04-19 14:34:02 +08:00
lian
cb05c2390e
add org_created_callback (#7746) 2025-04-19 14:32:51 +08:00
Michael An
de09b014a0
Merge pull request #7744 from haiwen/optimize/selection_ui_on_safari
disable text selection in grid view
2025-04-19 09:43:05 +08:00
Michael An
d733bbccc8
Merge pull request #7745 from haiwen/fix/selection_state_reset_after_switch_mode
reset dirent selection state after switch mode
2025-04-19 09:38:31 +08:00
zhouwenxuan
8cb5ecf83c reset dirent selection state after switch mode 2025-04-18 17:41:03 +08:00
zhouwenxuan
b3602c6fa5 disable text selection in grid view 2025-04-18 17:32:52 +08:00
Michael An
d7c3b459d2
Merge pull request #7741 from haiwen/optimize/enable_ocr_configuration
add ENABLE_SEAFILE_OCR, false by default
2025-04-17 17:25:12 +08:00
zhouwenxuan
d988c3f0cb optimize 2025-04-17 16:54:03 +08:00
zhichaona
a524757a92
set exdraw favicon (#7742) 2025-04-17 10:31:17 +08:00
zhouwenxuan
a3df5ddd0e add ENABLE_OCR, false by default 2025-04-16 17:08:15 +08:00
JoinTyang
b398c493c2
fix tag invalid bug (#7738) 2025-04-16 11:28:47 +08:00
Michael An
759189ae78 Merge branch '12.0' 2025-04-15 15:25:11 +08:00
Michael An
ca4aa8b0cb delete error translation 2025-04-15 15:24:47 +08:00
Michael An
344cd865b2 Merge branch '12.0' 2025-04-15 14:19:50 +08:00
Michael An
e157f8675e 12.0 update translation (#7737) 2025-04-15 14:16:22 +08:00
Michael An
ef8eb9137f
12.0 update translation (#7737) 2025-04-15 14:01:02 +08:00
Aries
c23a153818
Feature/show tags in search dialog (#7727)
* show related tags

* optimize

* fix eslint warning

* change searched tags background

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: Michael An <1822852997@qq.com>
2025-04-15 13:52:37 +08:00
Michael An
31e0b24e07
12.0 department set quota (#7736)
* 01 system admin department set quota

* 02 org admin department set quota
2025-04-15 13:46:17 +08:00
小强
da86a8e1b0 Fix reg compatibility issues 2025-04-15 11:02:42 +08:00
Michael An
1ec93e6f5a
Merge pull request #7735 from haiwen/fix-file-activity-page-UI
fix activity page scroll and css format
2025-04-14 17:30:01 +08:00
Michael An
6e6f49beed fix activity page scroll and css format 2025-04-14 17:10:58 +08:00
Michael An
1f77db68e5
Merge pull request #7733 from haiwen/fix-search-in-tags-page
fix search folder in tag view
2025-04-14 16:45:30 +08:00
Michael An
9249e4da17 fix search folder in tag view 2025-04-14 15:44:55 +08:00
Michael An
7a7079ed48
Merge pull request #7715 from haiwen/fix/file_tree_update_after_moving
update tree nodes after move files
2025-04-14 14:43:04 +08:00
Michael An
39f12f1279
Merge pull request #7730 from haiwen/optimize/libcontent_view_unnecessary_render
remove redundant path reset, remove unsafe lifecycle method
2025-04-14 14:29:20 +08:00
JoinTyang
59c719e64f
Merge pull request #7705 from haiwen/face_recognition_menu
face recognition menu
2025-04-14 11:45:58 +08:00
zheng.shen
17a4f2c637 update 2025-04-14 11:40:27 +08:00
lian
19d555880b
support view/edit csv file via onlyoffice (#7725)
* support view/edit csv file via onlyoffice

* update

* update
2025-04-11 20:11:22 +08:00
zheng.shen
8d837c8195 update 2025-04-11 17:20:59 +08:00
zhouwenxuan
8a18ada09a remove redundant path reset, remove unsafe lifecycle method 2025-04-11 17:03:28 +08:00
Michael An
8ebf4e7225
Merge pull request #7709 from haiwen/fix/file_tree
fix tree data update
2025-04-11 14:16:34 +08:00
Michael An
034f1e2a04
Merge pull request #7710 from haiwen/optimize/dropdown_menu
optimize dropdown menu open direction
2025-04-11 13:45:15 +08:00
Aries
c9984a8319
fix column data setting ui (#7728)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-11 13:05:09 +08:00
小强
05f5d13c20 update sdoc version 2025-04-11 11:20:38 +08:00
awu0403
6b51e54596
System metrics (#7700)
* add metric ui

* Update statistic-metrics.js

* handle metrics

* optimize ui

* update

* optimize ui

* update ui

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-04-11 11:09:25 +08:00
Michael An
1134469495
Remove deduplicated css (#7726)
* remove deduplicated css

* fix code warnings
2025-04-11 11:06:49 +08:00
zhouwenxuan
466e8f3a40 fix repeated build tree 2025-04-11 10:03:29 +08:00
zhouwenxuan
885b60f566 fix tree data update failed when switch repo by click searched folder 2025-04-11 10:03:29 +08:00
JoinTyang
272010d55f
optimize shared repos search (#7722)
* optimize shared repo search

* Update utils.py

---------

Co-authored-by: Daniel Pan <freeplant@gmail.com>
2025-04-10 17:44:23 +08:00
小强
7fb3b29e3a update sdoc editor version 2025-04-10 14:13:42 +08:00
awu0403
8e36f412da
Optimize fileter log (#7669)
* filter log v1

* optimize log user selector

* optimize ui and filter repo

* optimize ui of file update and permission

* admin filter completed

* optimize selector component

* optimize lint

* optimize code

* optimize filter

* update

* add filter group audit

* update parameters

* Update log-user-selector.js

* Update log-filter.css

* update var name

* update func name

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-04-10 13:55:15 +08:00
Michael An
efb1ac8286
Merge pull request #7724 from haiwen/fix-create-view-in-folder-bug
fix create view in folder bug
2025-04-09 23:00:33 +08:00
Michael An
c3fab488f6 01 fix create view in folder bug 2025-04-09 22:50:14 +08:00
小强
b599bed239 optimize code 2025-04-09 18:20:45 +08:00
Michael An
72f7e68bdc
fix readonly repo markdown editor (#7718) 2025-04-09 17:30:08 +08:00
Aries
3ec8c646b1
Optimize/seasearch filters (#7723)
* update seasearch filters

* seasearch support filter by owner and search_filename_only

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-09 17:02:16 +08:00
Ranjiwei
951698555d
Merge pull request #7701 from haiwen/org-register
send activity email when org register
2025-04-09 15:43:44 +08:00
awu0403
618f75ab13
Fix real lock (#7721)
* update

* fix get dirRouter

* Update lib-content-view.js

* Update lib-content-view.js

* Update lib-content-view.js

* update name

* Update lib-content-view.js

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-04-09 14:59:58 +08:00
杨顺强
2e5b2797b3
add excalidraw viewer (#7719)
Co-authored-by: 小强 <shuntian@xiaoqiangdeMacBook-Pro.local>
2025-04-09 11:16:01 +08:00
r350178982
2be71dd00b Update models.py 2025-04-08 17:05:48 +08:00
Michael An
1be01e5186
Change generate tags UI (#7712)
* 01 change header icon class

* 02 change tags UI

* 03 change modal header title
2025-04-08 16:47:35 +08:00
杨国璇
e67fc4a3d9
fix: metadata switch classname (#7717)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-04-08 16:35:47 +08:00
Aries
cb69c6662e
Feature/filters in search dialog (#7681)
* search results filters

* hide filters for seasearch

* optimize

* filter by suffix

* fix dropdown menu z-index

* update ui

* optimise css

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: Michael An <1822852997@qq.com>
2025-04-08 15:46:58 +08:00
杨国璇
53bedae485
feat: metadata status callback (#7716)
* feat: metadata status callback

* feat: optimzie code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-04-08 15:00:02 +08:00
Lewis
a865aecb6f
Merge pull request #7695 from haiwen/change-notice-icon
change notice close icon style
2025-04-08 14:58:07 +08:00
zhouwenxuan
ef4fbafa04 update tree nodes after move files 2025-04-08 14:32:29 +08:00
欢乐马
a1686622b3
Merge pull request #7714 from haiwen/12.0-add-forwarder-header-in-gunicorn
Update setup-seafile-mysql.py
2025-04-08 14:06:13 +08:00
llj
cf262f09db
[group] added full operation menus for all the department/group items… (#7706)
* [group] added full operation menus for all the department/group items in 'Files' page; fixed & improved all the operations

* remove useless state

* fix test lib

---------

Co-authored-by: Michael An <1822852997@qq.com>
2025-04-08 13:47:28 +08:00
r350178982
ee61bbd7c5 Update setup-seafile-mysql.py 2025-04-08 13:21:12 +08:00
Aries
b28e97970f
optimize view tree render (#7713)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-08 11:21:36 +08:00
Ranjiwei
baddc4bad7
Update single-selector.js (#7707) 2025-04-08 11:20:42 +08:00
Aries
641eb1fca5
check tags status before initialize repo tags (#7711)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-04-08 10:40:10 +08:00
小强
dfa86ebc45 update sdoc editor version 2025-04-07 17:28:54 +08:00
Michael An
ee09f7b0f8
Fix search tags not by RGB color (#7708)
* remove search tag by RGB color

* remove search tag by tag RGB color
2025-04-07 16:04:14 +08:00
zhouwenxuan
538bf10d18 optimize dropdown menu open direction 2025-04-07 15:52:37 +08:00
lian
04f948f7e4 send activity email when org register 2025-04-07 11:20:07 +08:00
zheng.shen
b58e48db0c update 2025-04-03 18:06:21 +08:00
zheng.shen
b27bbf6cce face recognition menu 2025-04-03 16:29:42 +08:00
Ranjiwei
09581a961a
Merge pull request #7026 from haiwen/lock-notification
Lock notification
2025-04-03 13:23:59 +08:00
Michael An
247a5b06ae change format 2025-04-03 09:28:42 +08:00
孙永强
6d32b4409b handle repo-update 2025-04-03 09:28:41 +08:00
孙永强
b27198bf02 update 2025-04-03 09:28:41 +08:00
孙永强
acd1a4a957 listen notification
optimize code

Update user-api.js

optimize code

optimize code

Update lib-content-view.js

Update lib-content-view.js

remove-userless-code

update settings

optimize cur code

add max number of reconnections
2025-04-03 09:28:36 +08:00
Michael An
56d4ebc785
migrate tags UI and delete old tags (#7699)
* basic codes

remove useless

* remove useless

* remove useless codes

* remove useless codes
2025-04-02 16:04:39 +08:00
小强
894679436c update sdoc-editor version 2025-04-02 15:59:12 +08:00
awu0403
073af84027
Add wiki freeze (#7673)
* freeze wiki page

* optimize

* optimize

* update

* update

* update

* update

* update

* Update wiki2.py

* update ui

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-04-02 13:05:17 +08:00
JoinTyang
cc99ce2e90
seasearch support fulltext search (#7696) 2025-04-02 11:47:06 +08:00
杨国璇
e332873d8e
Merge pull request #7697 from haiwen/fix-metadata-status
fix: metadata status
2025-04-01 18:36:10 +08:00
杨国璇
75460d1d7c fix: metadata status 2025-04-01 17:30:16 +08:00
lian
792135a224
remove share/upload links when user is deleted (#7694) 2025-04-01 16:54:25 +08:00
Michael An
f6ede9c8a7 change notice close icon style 2025-04-01 14:40:27 +08:00
Michael An
6614fd20a6
change register page style (#7693)
* change register page style

* remove useless file
2025-04-01 12:23:39 +08:00
awu0403
efe9ecce29
remove xmind (#7688)
Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-03-31 18:28:47 +08:00
Michael An
d58584e0d7
hide AI icon when SeafileAI is false (#7692)
* hide AI icon when SeafileAI is false

* fix test env
2025-03-31 18:27:31 +08:00
Ranjiwei
13098287d3
Update share_links.py (#7691) 2025-03-31 16:20:48 +08:00
Huang Junxiang
6d9d952079
revert: Python 3.8 compatibility (#7690)
* revert: Python 3.8 compatibility

* update(settings.py): default value for CACHE

* fix(settings.py): fetch redis cfg locations from env

* update requirements

* feat(gitcli::test.yml): support redis test

* feat(gitcli::test.yml): support redis test

* style(gitcli::test.yml): trailing blank line
2025-03-31 15:50:11 +08:00
Michael An
dd3f25e216
remove useless lib (#7689) 2025-03-31 15:01:19 +08:00
lian
c39c7c1f34
fix Uncontrolled data used in path expression (#7686)
* fix Uncontrolled data used in path expression

* Potential fix for code scanning alert no. 195: Uncontrolled data used in path expression

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Potential fix for code scanning alert no. 196: Uncontrolled data used in path expression

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-03-31 13:48:08 +08:00
zhichaona
700e933863
exdraw icon (#7687) 2025-03-31 11:48:13 +08:00
Huang Junxiang
71da2e685e
perf: read conf from env (#7680) 2025-03-31 10:50:25 +08:00
Ranjiwei
4ef9496557
update (#7682) 2025-03-31 09:53:39 +08:00
Michael An
2ed3b81934
Merge pull request #7684 from haiwen/fix/delete_image_failed
fix delete image failed when previewing in tag files view
2025-03-29 17:33:53 +08:00
Aries
8eb1c65ae3
fix edit detail failed in image previewer (#7685)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-29 17:27:24 +08:00
小强
da16173f35 update sdoc editor version 2025-03-29 16:07:39 +08:00
zhouwenxuan
a4835a9d7b fix delete image failed when previewing in tag files view 2025-03-29 15:57:34 +08:00
JoinTyang
beef890a47
fix file type (#7679) 2025-03-29 13:59:05 +08:00
JoinTyang
7723ef6fb7
add exdraw (#7678) 2025-03-29 13:40:23 +08:00
seafile-dev
5eb303c76b
fix Use of a broken or weak cryptographic hashing (#7671)
Co-authored-by: lian <imwhatiam123@gmail.com>
2025-03-29 13:16:01 +08:00
llj
c8026ddb6c
2fa redesign (#7677)
* ['enable 2fa' page] redesigned it

* ['2fa - backup tokens' page] redesigned it.

* ['disable 2fa' page] redesigned it
2025-03-29 13:14:29 +08:00
llj
872ae595b8
[shared dir view] display the loading icon in the content area when the users visit a folder (#7676) 2025-03-29 11:07:08 +08:00
Aries
796600eef6
collapse tag node by default (#7675)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-29 10:34:10 +08:00
小强
281a81cbd2 update sdoc translate 2025-03-28 18:01:13 +08:00
awu0403
ebe1c54153
Add group audit log 13 (#7661)
* add group audit log

* Update mysql.sql

* optimize parameters

* update

* update

* update

* optimize

* code optimize

* update

* Update operation-logs.js

* update

* Update models.py

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-03-28 17:51:29 +08:00
Guodong SU
29c8c12fa8
update newly created wiki page name (#7666) 2025-03-28 16:27:19 +08:00
小强
f7aaa0bff4 update sdoc editor version 2025-03-28 16:01:57 +08:00
欢乐马
f06981267d
rm CCNET_CONF_DIR (#7670) 2025-03-28 14:40:42 +08:00
JoinTyang
30036bf83f
fix invisable permission bug (#7672) 2025-03-28 14:27:15 +08:00
Aries
005ddb4dca
optimize tag editor ui (#7647)
* optimize tag editor ui

* optimize selection by up/down key

* optimize popover position

* fix searched tree nodes folding

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-28 11:45:17 +08:00
Aries
0abb343b4b
set fallback video thumbnail (#7668)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-27 16:26:18 +08:00
llj
0666b7a303
[system admin / users] redesigned the 'user filter' toolbar for the 'Database' tab (#7667) 2025-03-27 14:15:45 +08:00
zhichaona
3ec456fff5
time-base saving & toastTips (#7662)
* time-base saving & toastTips

* refactor hasChanged

---------

Co-authored-by: First <first@FirstdeMacBook-Pro.local>
2025-03-26 10:25:36 +08:00
Michael An
055bd575b9
change dirent detail value style (#7664) 2025-03-25 16:14:35 +08:00
Michael An
28fb4f4887
Change library setting entry permission (#7663)
* change lib setting AI entry

* change file name
2025-03-25 14:09:07 +08:00
Michael An
8d4377f85b Merge branch '12.0' 2025-03-24 17:33:12 +08:00
Michael An
18c72b9391
01 remove sort in share links page (#7660) 2025-03-24 16:50:07 +08:00
Ranjiwei
e872b4eff8
Merge pull request #7644 from haiwen/code-scan-db-api
fix "SQL query built from user-controlled sources"
2025-03-24 16:45:53 +08:00
r350178982
27c5d0294f remove-ch-characters 2025-03-24 16:31:40 +08:00
lian
5b63ef83f0 update 2025-03-24 16:31:40 +08:00
lian
db0e17b645 fix "SQL query built from user-controlled sources" 2025-03-24 16:31:40 +08:00
Michael An
2152dca689
gif image do not support rotate (#7641)
* fix image rotate file type

* fix code warning
2025-03-24 14:38:36 +08:00
lian
7b60cc38aa remove office convert test 2025-03-24 14:34:47 +08:00
lian
213927e1a7
fix Polynomial regular expression used on uncontrolled data (#7656)
* fix Polynomial regular expression used on uncontrolled data

* remove office convert code
2025-03-24 14:26:33 +08:00
zhichaona
ff7fd0f0d5
Add excl draw module 2 (#7658)
* init exceldraw module

* excalidraw demo

* i18n fixed bug

* exceldraw change to excalidraw

* lang setting

---------

Co-authored-by: 杨顺强 <978987373@qq.com>
Co-authored-by: First <first@FirstdeMacBook-Pro.local>
2025-03-24 14:11:58 +08:00
lian
ef9be3fed1 add new migrations files 2025-03-24 14:02:20 +08:00
JoinTyang
7d46c7aaa2
optimize wiki search permission (#7659) 2025-03-24 13:43:12 +08:00
小强
5e456c569a update sdoc version 2025-03-24 11:51:06 +08:00
Ranjiwei
75034bd9f1
Merge pull request #7655 from haiwen/code-scan-regex
fix Regular expression injection
2025-03-24 11:09:42 +08:00
awu0403
8151f7cf1c
fix clear text logging (#7640)
* fix clear text logging

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-03-24 11:01:49 +08:00
Ranjiwei
3c9394ced4
Merge pull request #7619 from haiwen/fix-Information-exposure-through-an-exception
fix Information exposure through an exception
2025-03-24 10:02:53 +08:00
Michael An
61cfae3d08
Merge pull request #7648 from haiwen/fix/path_navigation
reset state after navigation
2025-03-23 09:47:03 +08:00
Michael An
1841d799a2
Merge pull request #7650 from haiwen/feature/sort_tag_files
Feature/sort tag files
2025-03-23 09:41:50 +08:00
Michael An
b93e47b606 fix span key 2025-03-23 09:40:39 +08:00
zhouwenxuan
41a12fa90e optimize 2025-03-23 09:33:42 +08:00
zhouwenxuan
d27ea6be9f sort tag files 2025-03-23 09:33:42 +08:00
Michael An
e9c61f2bec
Merge pull request #7653 from haiwen/privacy-policy
update privacy-policy UI on login page
2025-03-21 18:22:55 +08:00
Michael An
baa144cf80 change font color 2025-03-21 18:21:56 +08:00
llj
cacef99651
[library decryption dialog] fixed the 'close' icon after the upgrade of seafile-ui.css (#7654) 2025-03-21 18:00:55 +08:00
lian
03e326d56f fix Regular expression injection 2025-03-21 17:44:56 +08:00
杨顺强
67e78e76e3 update dev script 2025-03-21 17:34:17 +08:00
lian
a998903f52 update 2025-03-21 17:05:39 +08:00
杨顺强
39aac08f3d update sdoc editor version 2025-03-21 16:29:33 +08:00
lian
0aa2d11f36 update privacy-policy UI on login page 2025-03-21 10:53:18 +08:00
欢乐马
592354b3cf
13.0 repo_metadata sql (#7652) 2025-03-20 18:36:34 +08:00
llj
db5a8b0695
[user settings] fixed 'Set WebDAV Password' dialog (#7651) 2025-03-20 17:42:28 +08:00
llj
34992f7ee7
[org admin / settings] fixed 'user default quota' setting and other 'input' setting items('org name', 'file ext white list'...) (#7649) 2025-03-20 17:12:44 +08:00
杨顺强
7365db5295 update sdoc editor version 2025-03-20 16:54:26 +08:00
zhouwenxuan
ec9e513699 reset state after navigation 2025-03-20 14:33:58 +08:00
Ranjiwei
fa79c2b3a3
Update internal_api.py (#7642)
* Update internal_api.py

* Update internal_api.py

* Update internal_api.py
2025-03-19 18:58:05 +08:00
lian
61426b04d9
fix Inclusion of functionality from an untrusted source (#7645) 2025-03-19 18:08:24 +08:00
llj
9a8b731780
[UI] fixed the color of .input-group-text's left border (#7643) 2025-03-19 16:34:53 +08:00
杨顺强
f6f39685a7 update sdoc editor version 2025-03-19 12:00:51 +08:00
欢乐马
a25bb24b05
github actions permission (#7624) 2025-03-18 18:56:30 +08:00
Aries
abf09b4593
show video thumbnail (#7639)
* show video thumbnail

* check preview permission

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-18 17:58:43 +08:00
Aries
618e25d1ab
Feature/all tags sort (#7618)
* sort menu

* sort tag tree

* optimize

* optimize

* update codes

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: renjie-run <rj.aiyayao@gmail.com>
2025-03-18 17:40:02 +08:00
Michael An
88de887f82 update sdoc editor version 2025-03-18 15:11:55 +08:00
Michael An
4cc3250c4a
Change metadata tag language setting style (#7636)
* change metadata lang setting

* change tip text
2025-03-18 14:57:58 +08:00
Michael An
742d7eb311 remove useless socket.io 2025-03-18 11:59:37 +08:00
Michael An
34b63a771d
Merge pull request #7637 from haiwen/fix-audit
fix npm audit
2025-03-18 11:49:34 +08:00
Michael An
0dda9864d0 fix npm audit 2025-03-18 11:45:08 +08:00
Michael An
0b698bf13c
Add metadata kanban collapsed tip (#7628)
* 01 change tip color to # 666

* 02 show tips when Kanban is collapsed

* 03 fix warning
2025-03-18 10:09:28 +08:00
llj
63c3b36b80
[pdf file view] fixup: make the top-right toolbar restore usable (#7634) 2025-03-17 22:16:33 +08:00
杨顺强
5c9edac317 update sdoc editor version 2025-03-17 21:14:41 +08:00
杨顺强
020a04d6ba update sdoc version 2025-03-17 20:38:36 +08:00
欢乐马
20866e43cc
update 13.0 sql (#7633) 2025-03-17 17:57:14 +08:00
llj
9263a445c5
[dir view] 'move', 'copy' dialogs: fixed the split line of the left and right panels after the upgrade of seafile-ui.css (#7632) 2025-03-17 17:49:46 +08:00
Michael An
acfc971ddd Merge branch '12.0' 2025-03-17 16:36:44 +08:00
llj
2c57086489
[PDF viewer] fixed the decryption dialog after the upgrade of seafile-ui.css(bootstrap v4 to v5) (#7630) 2025-03-17 15:47:43 +08:00
Michael An
61ae1a34fb
fix wiki sdoc outline style (#7629) 2025-03-17 15:04:41 +08:00
Ranjiwei
cba13592d0
update (#7627) 2025-03-17 14:45:35 +08:00
杨国璇
ae9e109c46
fix: reg (#7620)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-03-17 12:50:50 +08:00
杨国璇
339a149c0c
fix: location href url (#7621)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-03-17 12:49:40 +08:00
杨国璇
a53d4978d1
fix: password random (#7615)
* fix: password random

* fix: code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-03-17 12:31:33 +08:00
Ranjiwei
de14104af6
Update zip-download-dialog.js (#7625) 2025-03-17 11:56:33 +08:00
Michael An
e60eb83c93
Add repo setting tip when disable Metadata (#7623)
* Add repo setting tip when disable Metadata

* can not use button when disable metadata
2025-03-17 11:17:22 +08:00
Michael An
f44e6eea7d
Merge pull request #7614 from haiwen/dependabot/npm_and_yarn/frontend/babel/runtime-7.26.10
Bump @babel/runtime from 7.26.0 to 7.26.10 in /frontend
2025-03-17 11:04:43 +08:00
llj
19a5de3328
V5 rtl fixup (#7622)
* [system admin, org admin] departments: fixup for the 'New Department' button after the upgrade of seafile-ui.css(bootstrap v4 to v5)

* [wiki] removed unused code

* [Activities] fixup for mobile after the upgrade of seafile-ui.css(bootstrap v4 to v5)

* [repo snapshot] fixed the split line after the upgrade of seafile-ui.css(bootstrap v4 to v5)

* [system admin, org admin] statistic: fixed & improved the 'days' selecting toolbar
2025-03-15 23:41:14 +08:00
孙永强
3f0228e829 fix 2025-03-15 14:54:00 +08:00
欢乐马
9e2d9c49bb
13.0-sql (#7617) 2025-03-15 12:09:31 +08:00
dependabot[bot]
ca5225f6cc
Bump @babel/runtime from 7.26.0 to 7.26.10 in /frontend
Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.0 to 7.26.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 02:28:20 +00:00
Michael An
f1050952dd
Fix mobile UI in wiki trash dialog and share dialog (#7612)
* 01 change mobile wiki trash dialog

* 02 share dialog support mobile use

* 03 fix share to group click outside
2025-03-15 10:02:56 +08:00
llj
f46070e76d
['library folder permission' dialog] redesigned the 'add folder' panel (#7613)
- added a title, a 'back' icon, and a 'submit' button to the top
- removed the 'cancel' & 'submit' buttons in the bottom
- added gaps above & below the search input
2025-03-14 17:35:41 +08:00
llj
94553e0ef1
Mobile item op menu fixup (#7590)
* [my libs] fixed the operation menu items for library items in mobile after
reactstrap was upgraded from v8 to v9

- after the upgrade for reacstrap, the operation menu items fail to work

* [shared dir view] fixed the operation menu items in mobile

* [new component] added a new component 'MobileItemMenu' for items' operation menu in mobile

* [user settings] linked devices: fixed the operation menus in mobile

* [library content view] fixed the operation menus in mobile

* [department/group repo list, shared with all] fixed the operation menus in mobile

* [shared with me] fixed the operation menus in mobile

* [Favorites] fixed the operation menus in mobile

* [share admin / libraries] fixed the operation menus in mobile

* [share admin / folders] fixed the operation menus in mobile

* [share admin / share links] fixed the operation menus in mobile

* [share admin / upload links] fixed the operation menus in mobile

* [linked devices] fixed the operation menus in mobile

* [invite guest] fixed the operation menus in mobile

* [repo trash dialog] fixed the operation menus in mobile
2025-03-14 17:09:04 +08:00
llj
83be683b99
Misc UI fixup (#7610)
* [user settings] fixup for 'set webdav password'

* ['New Group' dialog] fixup
2025-03-14 17:08:21 +08:00
Aries
de2032e59c
fix record not found in image dialog (#7609)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-14 16:09:14 +08:00
Aries
0c29734c5f
Fix/record not found (#7606)
* fix path

* fix record not found after rename node

* fix path update

* fix rename dialog display

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-14 10:31:04 +08:00
Ranjiwei
d3c0a1c6ec
Update backends.py (#7605) 2025-03-14 09:40:41 +08:00
lian
bab0d71302
filter out wiki repos (#7608) 2025-03-13 18:20:54 +08:00
杨顺强
cb074ea304 update sdoc-editor version 2025-03-13 17:53:34 +08:00
Michael An
b34596accf
Merge pull request #7607 from haiwen/fix-form-and-close-UI
Fix form and close icon UI
2025-03-13 17:45:01 +08:00
Michael An
47852c1b4a update timestamp 2025-03-13 17:39:53 +08:00
Michael An
c12696b7c5 fix form and close UI 2025-03-13 17:37:20 +08:00
shenzheng-1
796d2c3144
Support ai instruction (#7595)
* support ai instruction

* update

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2025-03-13 17:31:16 +08:00
Michael An
d4af41cbce
Merge pull request #7602 from haiwen/fix-UI
fix table and nav UI after seafile-ui.css update
2025-03-13 11:45:38 +08:00
Michael An
40bd6b004b fix table and nav UI 2025-03-13 11:30:23 +08:00
Aries
68fa0a311e
optimize avatar in detail (#7601)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-12 23:02:37 +08:00
Michael An
5f412d7046 fix warnings 2025-03-12 20:17:00 +08:00
Michael An
db178150ae
Merge pull request #7587 from haiwen/13.0-use-new-seafile-ui
update seafile-ui.css
2025-03-12 18:23:25 +08:00
llj
d8156bbd25
[publish wiki] don't close the dialog after publishing the wiki (#7600) 2025-03-12 18:23:14 +08:00
Aries
bddbf3663b
optimize folder details display (#7596)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-12 17:46:36 +08:00
Aries
8dc9d0cdd1
update tag icon (#7598)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-12 17:46:02 +08:00
llj
8f99a7a651
[wikis] redesigned the 'No Wikis' UI(added a 'Add Wiki' button) (#7599) 2025-03-12 17:45:23 +08:00
Michael An
571aad3f53 add timestamp 2025-03-12 15:44:45 +08:00
Michael An
e33d14cbf7 update seafile-ui version 2025-03-12 15:41:38 +08:00
Michael An
bd92e7e5ef change modal header style 2025-03-12 15:41:38 +08:00
llj
cc3cb2f7a8
[system admin, org admin] departments: modified UI details for the 'New Department' button (#7594) 2025-03-12 15:38:29 +08:00
Aries
a2ee1fede2
Optimize/all tags view toolbar (#7593)
* all tags view toolbar

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-12 13:55:41 +08:00
杨国璇
d58878ec39
Merge pull request #7592 from haiwen/fix-metadata-modify-record-bug
Fix metadata modify record bug
2025-03-12 13:31:51 +08:00
Michael An
890183fe6c
Merge pull request #7591 from haiwen/fix/dropdown_menu_position
fix dropdown menu position
2025-03-12 12:06:35 +08:00
Aries
b9be45d151
display location column (#7569)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-12 12:01:31 +08:00
Michael An
75401f88f6 remove warnings 2025-03-12 11:46:41 +08:00
Michael An
752622f35e 02 fix metadata table view updateRecords to modifyRecords bug 2025-03-12 11:37:45 +08:00
Michael An
43cd67f8b1 01 fix tagsData type warning 2025-03-12 11:33:14 +08:00
zhouwenxuan
9bd4f15d94 fix dropdown menu position 2025-03-12 11:33:05 +08:00
杨顺强
a205d62cd7 update sdoc editor version 2025-03-12 10:45:01 +08:00
lian
578ede3e87
fix bug when get office feature (#7589) 2025-03-11 18:07:59 +08:00
Michael An
c10575b730
Merge pull request #7560 from haiwen/fix/column_dropdown_menu
fix column type dropdown menu
2025-03-11 18:04:48 +08:00
杨国璇
79fae81959
Merge pull request #7588 from haiwen/fix-sf-editor-x-icon
Fix sf editor x icon
2025-03-11 16:38:13 +08:00
杨国璇
0e492fad2a feat: update sdoc 2025-03-11 16:31:33 +08:00
杨顺强
634646bb36 update tldraw editor version 2025-03-11 16:25:44 +08:00
lian
d180e6e662
show library on Share Admin Folders page (#7586) 2025-03-11 16:24:01 +08:00
杨国璇
63830e1edc fix: sf editor x icon 2025-03-11 16:19:45 +08:00
杨顺强
b78e6767e9 update deps 2025-03-11 16:15:14 +08:00
杨顺强
33aa6d3c6b update deps 2025-03-11 15:03:25 +08:00
欢乐马
a58f877b94
github actions ignore repo_metadata (#7583)
* github actions ignore repo_metadata

* fix test

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-03-11 11:41:51 +08:00
JoinTyang
f664bed3a1
Merge pull request #7544 from haiwen/fix-lat-lng
Fix lat lng
2025-03-11 11:06:09 +08:00
awu0403
8ba1eedb47
Add org last activity time (#7575)
* update

* optimize code

* update ui

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-03-11 11:01:40 +08:00
孙永强
5fe0275432 fix lat lng
Update coord-transform.js
2025-03-11 09:55:45 +08:00
杨国璇
0e7cc9f3c5
Merge pull request #7582 from haiwen/fix-wgs84_to_gcj02
fix: wgs84_to_gcj02
2025-03-10 18:34:44 +08:00
杨国璇
e4b9430ce1 fix: wgs84_to_gcj02 2025-03-10 18:33:51 +08:00
Ranjiwei
770cc7e049
Merge pull request #7581 from haiwen/fix-file-update-bugs
Update send_file_updates.py
2025-03-10 18:32:44 +08:00
r350178982
23e04a7431 Update send_file_updates.py 2025-03-10 18:28:43 +08:00
Ranjiwei
60d48c1767
Merge pull request #7580 from haiwen/fix-file-send-updates-bug
Update send_file_updates.py
2025-03-10 18:23:09 +08:00
r350178982
102273b5d5 Update send_file_updates.py 2025-03-10 18:19:19 +08:00
Michael An
0e2627cde3 Merge branch 'master' into 13.0 2025-03-10 18:03:51 +08:00
Michael An
eabb0faed5
13.0 change dir operation dropdown menu (#7579) 2025-03-10 17:37:48 +08:00
JoinTyang
ed018320d5
use unified search instead msearch (#7574) 2025-03-10 17:36:44 +08:00
zhouwenxuan
7f77c99ba0 prevent dropdown menu to be obscured by popover 2025-03-10 14:46:01 +08:00
Ranjiwei
5aefc8373c
Merge pull request #7550 from haiwen/add-thumbnail-server
add thumbnail server
2025-03-10 14:43:15 +08:00
lian
699da59aff
rm get ldap user num (#7531) 2025-03-10 14:29:56 +08:00
Aries
4fb7552622
Optimize/tag formatter (#7578)
* optimize tag ui

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-10 14:28:23 +08:00
Michael An
55719ccbdf
remove react old defaultProps (#7576) 2025-03-10 14:27:08 +08:00
Aries
00707d1d05
Optimize/table view toolbar (#7567)
* optimize table view toolbar

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-10 14:17:09 +08:00
孙永强
0cafb5b3f8 optimize share link get thumbnail 2025-03-10 13:28:57 +08:00
杨顺强
7b293d0771 update sdoc editor version 2025-03-10 11:56:08 +08:00
Aries
5b1e3af9cb
fix dropdown menu ui of views_more_operation (#7577)
* fix dropdown menu ui of views_more_operation

* fix metadata-tree-view state error

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-10 11:48:03 +08:00
llj
b857a42c9a
[library content view] fixed the position of the operation menu for folder/file items displayed in the bottom of the page (#7565) 2025-03-10 10:21:14 +08:00
zhouwenxuan
9f36050ed9 fix dropdown menu z-index 2025-03-09 21:43:50 +08:00
zhouwenxuan
587f709568 fix column type dropdown menu 2025-03-09 21:34:37 +08:00
awu0403
2a78a8e8c3
add md test (#7494)
update test view

update load md

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-03-09 16:29:57 +08:00
Michael An
56901e2ac1
change readonly path UI (#7573) 2025-03-07 16:48:36 +08:00
awu0403
98e01baa8e
add metrics api (#7539)
* add metrics api

update

* update

* update

* Update __init__.py

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-03-07 16:42:23 +08:00
Michael An
ebeafd74ab
fix bug in external view image (#7572) 2025-03-07 16:07:11 +08:00
Ranjiwei
884b4b1ed2
initiate (#7523) 2025-03-07 13:40:24 +08:00
Aries
b2f2dc4d0b
fix record context menu doesn't show on row sequence (#7568)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-06 18:03:23 +08:00
欢乐马
65ee3f8a8c
Merge pull request #7566 from haiwen/fix-test
fix test
2025-03-06 15:30:20 +08:00
skywalker
0414a3f4bc fix github actions test 2025-03-06 15:22:49 +08:00
lian
3697c777f1
update error msg when transfer repo to dept failed (#7564) 2025-03-05 22:17:05 +08:00
欢乐马
2912a422bf
fix LOGGING_IGNORE_MODULES (#7562)
* fix LOGGING_IGNORE_MODULES

* Add default handler when used custom LOGGING
2025-03-05 17:03:25 +08:00
Michael An
a83adb7929
use image last modified time to remove browser cache (#7554)
* use image last modified time to remove browser cache

* remove useless
2025-03-05 17:01:54 +08:00
Michael An
df4e04d427
fix set repo history validation (#7563) 2025-03-05 16:55:39 +08:00
Ranjiwei
a8727ae408
Update core.py (#7561) 2025-03-05 15:10:03 +08:00
Michael An
e8de9ba848
remove useless divider in more menu (#7559) 2025-03-05 13:46:47 +08:00
Michael An
a8785ec879
change sdoc submenu direction (#7557)
* change sdoc submenu direction

* fix warnings
2025-03-05 12:43:04 +08:00
Michael An
4a4d28222b
fix search repo press enter (#7555) 2025-03-05 10:03:33 +08:00
Aries
e551580e67
optimize map interaction in side panel (#7553)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-04 20:23:02 +08:00
Aries
5014855e79
fix props of image dialog in map view (#7552)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-04 20:22:27 +08:00
Aries
9c32fe6833
fix layout of people view (#7551)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-04 20:21:31 +08:00
Aries
801a4a86cf
optimize arrow key down event on elements within tag editor (#7549)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-04 12:48:13 +08:00
Aries
1230dda8bd
optimize add view menu list (#7548)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-04 12:47:40 +08:00
Aries
cc0d3a281c
optimize add column interaction (#7546)
* fix icon props error

* optimize add column interaction

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-04 12:47:13 +08:00
lian
8d779db4a5
update error msg when rename file failed (#7547) 2025-03-04 12:24:08 +08:00
孙永强
fe331d95d2 add thumbnail server
preview heic

check jwt

fix share dir show image

update internal check

add-share-link-access-check

update
2025-03-03 17:21:16 +08:00
Michael An
6cb0522a4c Merge branch 'master' into 13.0 2025-03-03 17:15:35 +08:00
Michael An
a5dbce0f8b
01 no shared Wiki tip (#7540) 2025-03-03 17:05:39 +08:00
Michael An
870b7a6434
fix wechat and dingding department tree UI (#7545) 2025-03-03 17:04:56 +08:00
欢乐马
8ae5eedf03
rm ADD INDEX IF NOT EXISTS in sql (#7543) 2025-03-03 15:59:30 +08:00
JoinTyang
c9b82c70cd
read ai config from env (#7542) 2025-03-03 15:39:15 +08:00
Michael An
74c2b61b39
hide v1 wikis header when no v1 wikis (#7538) 2025-03-03 10:16:53 +08:00
JoinTyang
98ca860a16
Merge pull request #7537 from haiwen/support_use_dev_model
Support use dev model
2025-03-03 10:09:25 +08:00
杨顺强
c98fd74f66 fix plain markdown editor error 2025-03-02 17:43:42 +08:00
Aries
f942992b81
update grid images layout (#7536)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-01 17:31:54 +08:00
杨顺强
dc60da99c5 update sdoc version 2025-03-01 17:21:56 +08:00
杨顺强
27dae8ce74 update sdoc version 2025-03-01 16:10:30 +08:00
杨顺强
c59e7aeb2d update sdoc version 2025-03-01 15:43:54 +08:00
Michael An
0fadf04686 fix import package bug 2025-03-01 15:20:51 +08:00
zheng.shen
b9acd92b8a support use dev model 2025-03-01 15:20:22 +08:00
Michael An
bbe090bdb6 fix package diff 2025-03-01 15:08:45 +08:00
Aries
afd5d42abc
refactor insert column popover (#7535)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-03-01 12:29:48 +08:00
Jerry Ren
890880a5c8
refactor(metadata): remove ui-component (#7492)
1. ModalPortal
2. Icon
3. IconBtn
4. Loading
5. CenteredLoading
6. ClickOutside
7. SearchInput
8. Switch
9. CustomizeAddTool
10. SfCalendar
11. SfFilterCalendar
12. CustomizeSelect
13. CustomizePopover
14. FieldDisplaySettings
15. Formatters
16. remove duplicate codes
2025-03-01 10:12:48 +08:00
JoinTyang
67083238c2
optimize people cover cache (#7534) 2025-03-01 10:05:51 +08:00
杨国璇
21f96dd369
Merge pull request #7533 from haiwen/fix-toggle-metadata-file
fix: toggle metadata file
2025-02-28 17:58:00 +08:00
杨国璇
36dd38ffa0 fix: toggle metadata file 2025-02-28 17:52:37 +08:00
Michael An
7cda15e19c Merge branch 'master' into 13.0 2025-02-28 15:14:06 +08:00
Michael An
43c28a8466
update master translation (#7532) 2025-02-28 15:11:51 +08:00
Michael An
e7f43e16f2
fix detail.resolve_comment is vacant (#7530) 2025-02-27 18:25:47 +08:00
awu0403
3899577631
optimize notification (#7521)
* update

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-02-27 17:00:33 +08:00
Aries
2fb65580e4
update local storage in detail (#7529)
Co-authored-by: -g <aries@Mac.local>
2025-02-27 16:16:21 +08:00
Michael An
bbd4f15cca
13.0-fix click user notification tab (#7528) 2025-02-27 15:13:58 +08:00
Michael An
32a1639aca Merge branch 'master' into 13.0 2025-02-27 13:25:57 +08:00
杨顺强
b499dd87cc update sdoc-editor version 2025-02-27 11:44:38 +08:00
Aries
c9e2dea463
fix tag files selection area (#7527)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-27 11:33:19 +08:00
lian
a7fb61a4d4
Org active (#7496)
* admin set org active/inactive

* update

* update

* update
2025-02-27 11:12:48 +08:00
Michael An
ea3a7798b9
change icons (#7526) 2025-02-27 10:34:58 +08:00
Aries
314512d353
Feature/add portrait to groups (#7497)
* fix close button error

* update people selection ui

* add people to groups

* optimize people selector ui

* batch update

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-26 21:23:42 +08:00
Aries
295f694baa
show metadata in image previewer (#7525)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-26 16:15:17 +08:00
Aries
d4b2e75b7e
fix selected item match error in recently used list (#7524)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-26 16:08:22 +08:00
Ranjiwei
33c4c76608
Merge pull request #7470 from haiwen/link-invite-to-join-group-13.0
invite user
2025-02-26 13:49:16 +08:00
Michael An
a3a609d89d
change dir operation icon style (#7522) 2025-02-26 13:05:24 +08:00
Michael An
e44962be9a change button style 2025-02-26 11:42:54 +08:00
Michael An
47f016e6e9 change dialog style 2025-02-26 10:10:19 +08:00
杨顺强
88bb921c91 merge master to 13.0 2025-02-26 10:07:17 +08:00
杨顺强
0035fd0f04 update seafile-editor version 2025-02-26 09:52:02 +08:00
Aries
9bcbab1761
support multiple selection by shift key in gallery (#7518)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-25 17:53:04 +08:00
Michael An
96fd59b54e
change search button style (#7519) 2025-02-25 17:15:29 +08:00
Jerry Ren
453deac354
Merge pull request #7520 from haiwen/fix-warnings
fix warnings
2025-02-25 17:00:33 +08:00
renjie-run
966fc65a9d fix warnings 2025-02-25 16:56:16 +08:00
Aries
30d0a34afa
Feature/tags view file operations (#7464)
* add basic file operations

* add tag view toolbar operations

* add share and rename

* optimize

* optimize

* update codes

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: renjie-run <rj.aiyayao@gmail.com>
2025-02-25 16:48:59 +08:00
Ranjiwei
49896f53ef
Merge pull request #7082 from haiwen/update-user-notifications
Update user notifications
2025-02-25 16:21:07 +08:00
skywalker
1d8df18ca0 fix seafile-fuse.sh 2025-02-25 13:47:51 +08:00
Aries
d1ac836de6
update new tag icon (#7516)
* update new tag icon

* fix font style

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-25 12:05:51 +08:00
Michael An
617a8fab72 Merge branch 'master' into 13.0 2025-02-25 12:01:46 +08:00
Michael An
ec68ff1bcd
fix readonly shared repo dir toolbar name (#7517) 2025-02-25 11:57:58 +08:00
Aries
669c423b07
add feedback (#7514)
* add feedback

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-25 11:39:29 +08:00
Michael An
4385020918
Add dir operation icon (#7515) 2025-02-25 11:33:22 +08:00
r350178982
67dd7e5eed Update models.py 2025-02-25 10:44:11 +08:00
r350178982
a8a2fd6382 update 2025-02-25 10:42:47 +08:00
Aries
d66695954d
update view type change tips (#7513)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-25 09:52:32 +08:00
Aries
13912eea7e
update merge tags selector ui (#7512)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-24 18:26:19 +08:00
llj
b461005c18
['manage group members' dialog] keep this dialog open after clicking the 'cancel' button in the 'select group members' dialog (#7511) 2025-02-24 17:51:43 +08:00
Aries
0a11ce8282
fix context menu position (#7510)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-24 17:05:48 +08:00
llj
b4ed45225b
[library content view] move 'share' into the dropdown menu for each folder/file item (#7508) 2025-02-24 16:54:19 +08:00
Aries
2f4b1e76e5
optimize ui (#7509)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-24 16:04:06 +08:00
Michael An
cd5f4511ab update react-image-lightbox version 2025-02-24 15:13:03 +08:00
Michael An
78cb08d5a1
external link fix abuse report style (#7507)
* external link fix abuse report style

* change codes
2025-02-24 14:03:09 +08:00
Aries
5bca4562fb
fix ui (#7506)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-24 14:02:26 +08:00
Michael An
7d421b9afd
fix create group repo style (#7505) 2025-02-24 12:11:52 +08:00
Michael An
89c37f7656
fix context menu position error in react 18 (#7504) 2025-02-24 12:11:05 +08:00
Aries
ebc97ad6ac
fix selectedRange state error (#7499)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-24 11:34:48 +08:00
awu0403
35f30ab1cd
update metadata map (#7482)
* update metadata map

optimize

* optimize

* update v2

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-02-24 11:33:04 +08:00
杨顺强
bdc9aa24ec
optimize code (#7503) 2025-02-24 10:55:17 +08:00
孙永强
9807cbd752 remove sdoc notification count cache 2025-02-24 10:12:16 +08:00
Michael An
b5a52668e9 change notification dialog style 2025-02-24 10:12:16 +08:00
Michael An
3f17045e47 change UI 1 2025-02-24 10:12:16 +08:00
孙永强
4f87a9e477 optimize ui 2025-02-24 10:12:16 +08:00
r350178982
1fb671ad31 Update notifications.py 2025-02-24 10:12:16 +08:00
r350178982
3bde4555d0 update 2025-02-24 10:12:16 +08:00
r350178982
7d1e9e11ea Update notifications.py 2025-02-24 10:12:16 +08:00
孙永强
ef77d90280 add update all notification API 2025-02-24 10:12:16 +08:00
孙永强
f4558a0f46 optimize 2025-02-24 10:12:16 +08:00
孙永强
b1c44da366 optimize 2025-02-24 10:12:16 +08:00
孙永强
631320c889 complete 2025-02-24 10:12:16 +08:00
孙永强
0f77313761 update 2025-02-24 10:12:16 +08:00
孙永强
27dcce694b Update notifications.py 2025-02-24 10:12:13 +08:00
孙永强
f6f606cb8f optimize 2025-02-24 10:01:45 +08:00
孙永强
f00b81f8d8 invite user 2025-02-24 10:01:41 +08:00
Michael An
175803baba
change table column icon color (#7501) 2025-02-22 16:00:30 +08:00
Aries
a39765072c
fix tag category selection state (#7500)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-21 21:20:18 +08:00
llj
f4be8814c5
[repo trash] redesigned it for mobile (#7502) 2025-02-21 21:17:49 +08:00
杨顺强
e1eb01b439 update sdoc version and sdoc translate 2025-02-21 18:26:13 +08:00
awu0403
d91cdad79e
Update transfer repo add audit log (#7480)
* add admin log

* update

* update

* update

* add org transfer log

* optimize

* Update file-transfer-log.js

* add db_index

* add-operator

* update

* file --> repo

* Update mysql.sql

* update

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-02-21 18:12:25 +08:00
Aries
83d615d3ed
Feature/view conversion (#7483)
* support view conversion

* optimize

* fix view properties migration

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-21 16:42:18 +08:00
llj
30ecd7a385
[wiki] added a resizing bar (enable users to resize the width of side… (#7489)
* [wiki] added a resizing bar (enable users to resize the width of side panel and main panel)

* [wiki] main panel: fixed conditions for 'DocInfo'

* [wiki] resize: make it independent of the other pages
2025-02-21 12:07:39 +08:00
JoinTyang
2b297f4f77
jwt token check (#7158)
* jwt token check

* remove-exp-for-paste-link

* update

* Update views.py

---------

Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2025-02-21 11:48:47 +08:00
lian
15e9d678f0
update get share link permission (#7493)
* update get share link permission

* update
2025-02-20 20:23:06 +08:00
Michael An
6d8a61627e Merge branch 'master' into 13.0 2025-02-20 17:59:59 +08:00
Michael An
90a4cf40ea
fix transfer department repo to another department (#7491) 2025-02-20 17:56:00 +08:00
Aries
7a94d66511
add image deletion button on table view image previewer (#7475)
* add image deletion button on table view image previewer

* optimize

* check permission before delete image

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-20 16:25:51 +08:00
Aries
27abc38e7e
update drag handler (#7490)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-20 12:09:22 +08:00
欢乐马
cebadb9769
Merge pull request #7433 from mirfire/fix/seaf-fuse-selfmatching
fix: fix seaf-fuse.sh pgrep matching with itself
2025-02-20 10:37:41 +08:00
skywalker
481365fe64 fix seaf-import 2025-02-20 10:35:11 +08:00
杨顺强
5a76c6b2ae update sdoc version and sdoc translate 2025-02-19 16:48:35 +08:00
Michael An
71966dd729 Merge branch 'master' into 13.0 2025-02-19 15:02:15 +08:00
Michael An
5ca5acc277
change sys-admin statistics button style (#7487) 2025-02-19 14:55:48 +08:00
Aries
659ada62d3
fix selection state error after copy items (#7474)
* fix selection state error after copy items

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-19 14:55:01 +08:00
Aries
d9019be22f
fix currentMode state modified by accident (#7471)
* fix currentMode state modified by accident

* change parameter text

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: Michael An <2331806369@qq.com>
2025-02-19 14:48:47 +08:00
Aries
9692c5e398
optimize thumbnail size in gallery (#7486)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-19 13:46:24 +08:00
Michael An
bc2c7c80b7
fix shared dir page save to repo bug (#7485) 2025-02-19 13:45:36 +08:00
欢乐马
e09ce188aa
custom oauth (#6269) 2025-02-19 12:01:19 +08:00
Michael An
367fa05a21
fix system admin department link (#7484) 2025-02-19 11:49:16 +08:00
Aries
c957dd238f
Feature/modify tag links by drag and drop (#7469)
* change tags link by drag and drop

* optimize

* optimize drag image

* optimize drag effect

* update codes

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: renjie-run <rj.aiyayao@gmail.com>
2025-02-19 10:24:30 +08:00
Jerry Ren
680006a883
refactor(react): replace findDOMNode by ref (#7479) 2025-02-18 16:56:37 +08:00
Michael An
e242c77202
fix 13.0 org-admin reach-router bug (#7481) 2025-02-18 14:04:39 +08:00
杨顺强
02c2b98299 fix tldraw style 2025-02-18 10:43:29 +08:00
Jerry Ren
d8c6c0c684
Merge pull request #7477 from haiwen/fix-edit-tag-error
fix(tags): commit error
2025-02-18 10:32:40 +08:00
Michael An
2b74a02a67
13.0 fix reactstrap Label style (#7478)
* 01 change dialog Label

* 02 change form-label style
2025-02-18 10:31:05 +08:00
Michael An
b386240f28
fix paginator style (#7476) 2025-02-17 20:39:27 +08:00
renjie-run
9dce302e1e fix(tags): commit error 2025-02-17 18:25:26 +08:00
杨顺强
2e0fdf1049 fix change left forder node bug 2025-02-17 17:36:34 +08:00
杨顺强
cdd059fdb9 fix tldraw editor bug 2025-02-17 17:04:14 +08:00
Michael An
1cb4e37207
clean seahub_react.css cache (#7473) 2025-02-17 17:02:37 +08:00
Michael An
ef8cd06f72
fix reactstrap 9.x DropdownMenu style (#7472) 2025-02-17 17:02:02 +08:00
llj
79413cb4fd
Shared dir view redesign (#7468)
* [shared dir view] redesigned it

- changed to a full-screen 'side panel & main panel' layout
- added a 'folder tree' panel, a current path bar with an operation dropdown
  menu, a toolbar for selected items, a 'view mode' menu, and a 'sort'
  menu

* [shared dir view] added a resizing bar (enable users to resize the width of side panel and main panel)

* [shared dir view] path bar: added a 'plus' icon to the dropdown menu toggle when 'upload' is offered in the menu

* [shared dir view] folder tree: cleaned up the code

* [shared dir view] improved 'visit a folder'

* [shared dir view] improvements & cleanup
2025-02-17 14:58:30 +08:00
Michael An
9c992dbfc2
fix-system-admin-repo-owner-link (#7463) 2025-02-17 11:53:56 +08:00
杨国璇
06410d217d
feat: react warning (#7467)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-02-17 11:50:29 +08:00
杨顺强
91b9a7840f update sdoc version and sdoc translate 2025-02-17 11:15:07 +08:00
杨顺强
21162095fb merge master to 13.0 2025-02-17 10:16:35 +08:00
skywalker
df2d8def00 dist-13.0 2025-02-17 10:03:16 +08:00
杨国璇
23c400a437
fix: scheduleUpdate (#7466)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-02-15 16:11:08 +08:00
杨顺强
e85866b7a9 update sdoc version and sdoc translate 2025-02-15 16:01:59 +08:00
杨国璇
236ecf987a
feat: metadata ui 1.0 (#7465)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-02-15 15:50:15 +08:00
杨顺强
95a1df9d37 update sdoc version and sdoc translate 2025-02-15 14:58:59 +08:00
Jerry Ren
e8bfdd6057
fix(tags): init tags tree error (#7462) 2025-02-14 21:16:40 +08:00
杨顺强
e2e2e98c43 update sdoc-editor version 2025-02-14 14:14:05 +08:00
杨顺强
ccab6f1552
Update react version 3 (#7453)
* update react version

* update reactstrap

* optimize code

* update react-select version

* update react-responsive

* update react-chartjs version

* update qrocde version

* update seafile-editor version

* update tldraw editor version

* fix code bug
2025-02-14 14:04:25 +08:00
Michael An
508e77d125
fix system admin search repo (#7461) 2025-02-14 13:58:18 +08:00
JoinTyang
1ac807e6f7
Merge pull request #7429 from haiwen/feat/add-key-face-photo
feat: add  make key face photo sup
2025-02-13 17:31:04 +08:00
cir9no
82b4fb1ea3 feat: add make key face photo sup 2025-02-13 14:52:03 +08:00
Michael An
ad5ddebaa6 update version: react-image-lightbox 2025-02-13 14:34:54 +08:00
Michael An
49dd313899
fix heic image preview in external link page (#7459) 2025-02-13 14:18:01 +08:00
Ranjiwei
3ff13909cd
add_social_auth_user (#7458) 2025-02-12 18:27:59 +08:00
杨顺强
90030a8d11 update sdoc version and sdoc translate 2025-02-12 17:03:23 +08:00
Michael An
8504134ad6
change wiki and invitation pages add icon (#7457) 2025-02-12 13:44:09 +08:00
lian
24147256b4
mark action url safe (#7454) 2025-02-11 22:00:14 +08:00
杨顺强
ef7ac1dd8b update sdoc version and sdoc translate 2025-02-11 16:05:13 +08:00
lian
93ecea748d update disable pwd login 2025-02-11 15:01:48 +08:00
lian
9b43c32866 check if can_create when create folder 2025-02-11 11:25:08 +08:00
Michael An
f9e0c467ef
update translation (#7451) 2025-02-10 11:18:57 +08:00
Jerry Ren
4c15cafb72
feat(tag): support search tags (#7450) 2025-02-09 17:59:27 +08:00
lian
89382b4efb
add org url (#7449) 2025-02-08 18:24:26 +08:00
Michael An
25bf640426
change admin page department group link (#7442) 2025-02-08 18:23:21 +08:00
awu0403
c81d79b8bc
format log (#7441)
* format log

* update

* optimize

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-02-08 18:04:53 +08:00
Aries
cb3af6e66a
kanban supports collapse (#7437)
* kanban supports collapse

* optimize

* optimize ui

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-08 17:56:36 +08:00
Aries
65ef9f704b
add_child_tags context option (#7448)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-08 16:16:13 +08:00
lian
3998d3be44
update community to pro url (#7447)
* update community to pro url

* update
2025-02-08 14:35:13 +08:00
杨顺强
2b08b68f7a update sdoc version and sdoc translate 2025-02-08 13:55:26 +08:00
Michael An
3472206e83
fix metadata change format bug (#7446) 2025-02-08 11:58:05 +08:00
shenzheng-1
dad238c9a5
add people photo (#7408)
* add people photo

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2025-02-08 11:36:09 +08:00
Aries
92f27a1601
fix cursor style (#7445)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-08 11:14:23 +08:00
llj
3d7b8b3a6b
Image zoom rotate (#7425)
* [image file view] added a zoomer(enable users to zoom in/out the image)

* [image file view] added 'rotate'(rotate the image in counter-clockwise direction and save it)

* [image file view] fixup

* [image file view] keep the 'prev/next' icons displayed on top of the image which is zoomed in

* [image file view] improved the display of the 'image saved' tip

* [image file view] don't offer 'zoom in/out' & 'rotate' for images with errors

* [image file view] don't offer 'rotate' for PSD & HEIC files

* [image file view] enable HEIC files to be viewed online
2025-02-08 10:27:34 +08:00
Aries
d65e86731e
fix scroll position (#7444)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-08 09:46:51 +08:00
Aries
5b5423c63f
Optimize/drag and drop UI (#7432)
* update the sorting effect in views tree

* update table header sort ui

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-07 18:50:08 +08:00
Aries
db1ea4f07d
show date tag in all mode (#7443)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-07 17:09:37 +08:00
Jerry Ren
10770501a4
fix(tag): update tag files link via set file tags (#7439) 2025-02-07 16:42:12 +08:00
lian
ae700f7fa9
oauth disable pwd login (#7424) 2025-02-07 16:33:35 +08:00
Michael An
5abb623306
update translation (#7440) 2025-02-07 16:31:01 +08:00
杨国璇
a87e662f55
Merge pull request #7434 from haiwen/optimize/file_name_in_table_view
Optimize/file name in table view
2025-02-07 14:00:44 +08:00
杨国璇
2033dbc19c feat: optimize code 2025-02-07 13:47:08 +08:00
zhouwenxuan
6cbfe0efd4 update ui component version 2025-02-07 10:32:21 +08:00
zhouwenxuan
7e70826a3b add click event handler on file name formatter 2025-02-07 09:42:01 +08:00
Ranjiwei
b8a1e0db0b
Update repos.py (#7435) 2025-02-06 14:09:30 +08:00
llj
6eb86005f7
[library history setting] modified the UI of the 'setting disabled' tip (#7436) 2025-02-06 13:07:15 +08:00
Michael An
a96990ebbc
fix heic format rotate image (#7430) 2025-02-06 10:50:09 +08:00
Aries
fef0d63712
fix recently used list (#7393)
* fix recently used list

* fix recently used list

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-02-06 10:11:32 +08:00
mirfire
a2ea9c5383 fix: fix seaf-fuse.sh pgrep matching with itself
The script `seaf-fuse.sh` uses pgrep to check if `seaf-fuse` is already running and therefore matches with itself, as both the process and script contain `seaf-fuse`. So the script can never run.

This change changes the pgrep pattern from `seaf-fuse` to `bin/seaf-fuse` so that the binary is matched instead of both script and binary.
2025-02-05 13:47:18 +01:00
Jerry Ren
94914009c1
feat(tag): display tags sidebar with tree (#7428) 2025-01-26 17:56:49 +08:00
Aries
01791be348
not display suffix column in table (#7427)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-26 15:55:27 +08:00
Aries
94e19c58c0
Optimize/map cluster (#7426)
* optimize cluster position, clean up map view toolbar

* fix cluster position, optimize controller ui

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-26 15:54:20 +08:00
Michael An
f8112c0306
Fix some style (#7423)
* 01 fix wiki custom-url-inputs style

* 02 fix library icon style
2025-01-24 14:12:56 +08:00
Michael An
bddd73411e
fix modify column width (#7422)
* fix modify column width

* fix warnings
2025-01-23 17:44:03 +08:00
Aries
0e11cdf7b9
filter by suffix (#7364)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-23 15:52:50 +08:00
Jerry Ren
b7d9f781fd
refactor(tag): parent tag files count includes children (#7420) 2025-01-23 14:37:57 +08:00
Aries
076d147d4e
Optimize/map marker cluster (#7421)
* click overlay to preview

* updatea cluster plugin

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-23 14:35:46 +08:00
Ranjiwei
0b0e471c90
Fix org smart link (#7416)
* Update file.py

* Update file.py
2025-01-23 13:40:47 +08:00
lian
cccd3a494a
update create group owned library logic (#7403) 2025-01-23 12:52:00 +08:00
Aries
7626ea3d89
optimize comtext menu (#7417)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-23 12:50:26 +08:00
Michael An
f590e7741d
Fix download file bug in grid mode right click (#7418)
* fix download file use different API

* change error message
2025-01-23 12:49:54 +08:00
Aries
689b5cba83
interchange position of slider and groupby setter (#7414)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-23 10:42:43 +08:00
Michael An
ec507d07eb
fix my libs icon style (#7415) 2025-01-23 10:41:04 +08:00
Daniel Pan
2467734990
Revert "add repo in right tool panel (#7410)" (#7413)
This reverts commit b07f33e07c.
2025-01-23 09:50:56 +08:00
Daniel Pan
9cf1e9564a
Revert "Revert "[library list] added 'plus' icon to the toggle of the dropdow…" (#7412)
This reverts commit b606f65f4e.
2025-01-23 09:39:38 +08:00
Aries
a4738295db
add folder in empty view tree (#7357)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-22 17:53:03 +08:00
Michael An
c48aa43ead
fix Input auto complete with no name when create repo (#7411) 2025-01-22 16:10:19 +08:00
杨顺强
efee8f88ec update sdoc version and sdoc translate 2025-01-22 16:01:05 +08:00
Guodong SU
12e2151c11
feat(video):add video file (#7394)
* feat(video):add video file

* optimize code

* optimize code

* optimize code

* add parameter
2025-01-22 15:42:05 +08:00
Michael An
b07f33e07c
add repo in right tool panel (#7410) 2025-01-22 14:38:40 +08:00
Aries
cfe507f178
record the capture info collapse status (#7392)
* record the capture info collapse status

* fix typo

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-22 11:23:33 +08:00
Huang Junxiang
55c1d366cb
opt: remove code for migrating from ce (#7406) 2025-01-22 11:18:42 +08:00
lian
b51c26c4fa
use user limit to get org traffic limit (#7405) 2025-01-21 20:07:30 +08:00
lian
a49b940ae8
update customer permission of create (#7404) 2025-01-21 16:52:50 +08:00
Jerry Ren
16b361f932
feat(tag): support merge tags (#7402) 2025-01-21 15:08:52 +08:00
Daniel Pan
b606f65f4e
Revert "[library list] added 'plus' icon to the toggle of the dropdown menus …" (#7401)
This reverts commit 798384a638.
2025-01-21 13:39:10 +08:00
Michael An
9900ffc43a
fix trash table bug (#7399) 2025-01-21 11:30:27 +08:00
llj
798384a638
[library list] added 'plus' icon to the toggle of the dropdown menus at the end of the headings (#7400) 2025-01-21 11:26:56 +08:00
杨顺强
458d783195 update sdoc version and sdoc translate 2025-01-21 10:12:06 +08:00
Ranjiwei
2b6e0522b1
update (#7396)
* update

* Update search_user.py
2025-01-20 21:06:06 +08:00
JoinTyang
cc597b331c
script can update seasearch index (#7395) 2025-01-20 18:07:31 +08:00
杨顺强
3a71b7d80b
fix insert wiki title bug (#7398) 2025-01-20 17:57:39 +08:00
Michael An
22c08df773
fix sdoc history filename escapejs (#7397) 2025-01-20 17:47:32 +08:00
杨顺强
fae2040680 update sdoc version and sdoc translate 2025-01-20 15:58:48 +08:00
杨顺强
9d2047a4be update sdoc version and sdoc translate 2025-01-20 13:59:44 +08:00
lian
32406114d9
update adfs user login error msg (#7390)
user "The number of users exceeds the limit."
2025-01-20 12:13:57 +08:00
Aries
a13f101e40
disable sort within a single group (#7389)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-20 09:48:50 +08:00
杨顺强
41c09b95c6 update sdoc version and sdoc translate 2025-01-20 08:02:27 +08:00
欢乐马
0076228ef5
LDAP_FOLLOW_REFERRALS (#7384) 2025-01-19 10:40:37 +08:00
杨顺强
4d8601a5f5 update outline translate 2025-01-18 16:53:54 +08:00
Guodong SU
137a46eabd
style(ui): fix markdown editor outline style (#7379)
* style(ui): fix markdown editor outline style

* optimize style
2025-01-18 16:48:59 +08:00
awu0403
1c8955723d
fix thumbnail icc profile (#7338)
* fix thumbnail icc profile

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-01-18 09:56:05 +08:00
llj
9760eca25c
Dir view permission fixup (#7383)
* ['dir view' page] fixed permission for creating files when the library is empty

* ['dir view' page] fixed permission for rotating images in the image preview dialog
2025-01-17 17:35:28 +08:00
Michael An
41efd64872
fix repo history set limit (#7382) 2025-01-17 17:33:50 +08:00
杨顺强
859074c5d1 fix wiki editor bug 2025-01-17 16:13:52 +08:00
Aries
07cec873e6
make gallery year tag clickable (#7381)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-17 16:09:38 +08:00
Michael An
7d4a9f4575
Fix about dialog close button style (#7380)
* 01 fix about dialog style

* 02 fix guide for new dialog
2025-01-17 16:00:32 +08:00
杨顺强
cf4856d597 optimize code 2025-01-17 15:31:55 +08:00
Jerry Ren
fdbf48fed3
feat(tag): support select multiple rows (#7378) 2025-01-17 14:31:35 +08:00
Aries
724c96bdf8
optimize gallery (#7376)
* optimize

* optimize

* fix container width calculation

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-17 14:01:03 +08:00
Jerry Ren
5a5005f486
refactor(tag): open tag files via click name (#7377) 2025-01-17 13:35:38 +08:00
Jerry Ren
178ff372b7
fix(tag): selected tags width (#7375) 2025-01-17 10:25:37 +08:00
Jerry Ren
3250ca151c
feat(tag): support add child tag (#7374) 2025-01-17 10:10:45 +08:00
Michael An
fdf720ffd5
fix 'Group info' text (#7373) 2025-01-17 10:09:39 +08:00
杨顺强
3a743f2d70 update sdoc version and sdoc translate 2025-01-16 17:30:39 +08:00
杨顺强
9d2367b35b update sdoc version and sdoc translate 2025-01-16 14:27:25 +08:00
Michael An
ca045d269e
update translation (#7372) 2025-01-16 10:46:04 +08:00
Ranjiwei
88232cf064
handle-role-is-manual-set (#7371) 2025-01-16 10:00:49 +08:00
llj
e48a9cad97
[system / org admin - departments] improved UI (the tree panel, 'New department', ...) (#7370) 2025-01-15 22:32:24 +08:00
欢乐马
76dc9a71a8
update import LDAP settings (#7368) 2025-01-15 18:20:31 +08:00
杨顺强
492546dc65 update sdoc version and sdoc translate 2025-01-15 18:07:27 +08:00
lian
4c25402e9e
fix login failed as soon as license limit is reached (#7369) 2025-01-15 18:02:02 +08:00
Jerry Ren
7fc2f193e6
feat(tag): support display tags with tree (#7365) 2025-01-15 16:47:12 +08:00
skywalker
5574252dca fix github actions test 2025-01-15 16:41:42 +08:00
Michael An
019cf75a67
Share to user support select user from department (#7361)
* update

* remove 'All users' in org user department

* change style

---------

Co-authored-by: lian <imwhatiam123@gmail.com>
2025-01-15 13:57:24 +08:00
Michael An
3a4ce4e59f
change grid file icon size to 80px (#7363) 2025-01-15 11:53:00 +08:00
杨顺强
61215e4376 update sdoc version and sdoc translate 2025-01-14 15:39:19 +08:00
杨顺强
1400730944 optimize sdoc i18n 2025-01-14 14:52:35 +08:00
杨顺强
fb850e2a16 fix sdoc translate bug 2025-01-14 14:34:54 +08:00
Aries
a6950355ea
update overScan (#7360)
* update overScan

* update thumbnail size in year mode

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-14 14:27:18 +08:00
awu0403
256b1ec70d
fix admin delete repo (#7352)
* fix admin delete repo

* delete useless codes

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: Michael An <2331806369@qq.com>
2025-01-14 13:55:18 +08:00
llj
bb48f9dd57
[image view with dialog] improved the tip (#7359)
* [image view with dialog] improved the tip

* [image view with dialog] updated the tip
2025-01-14 13:54:01 +08:00
杨顺强
88c99173f1 fix sdoc translate bug 2025-01-14 13:37:22 +08:00
杨国璇
2bc89a67d9
fix: day images selected (#7355)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-13 18:19:33 +08:00
lian
20c81de6cf
Share to user support select department (#7327)
* select department user when share repo to user

* update

* update

* 01 fix code warnings

* 02 change dialog style

* 03 manage group members support select department user

---------

Co-authored-by: Michael An <2331806369@qq.com>
2025-01-13 18:18:58 +08:00
feiniks
dd3003a693
Catch exception in thread pool when migrate repos (#7320)
* Catch exception in thread pool when migrate repos

* Add comment and use different exit code

* Check exception only

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2025-01-13 16:58:56 +08:00
欢乐马
de75571ae6
update scripts read env (#7356) 2025-01-13 15:48:10 +08:00
JoinTyang
b0f56adc61
fix file search settings bug (#7354) 2025-01-13 15:46:25 +08:00
Aries
c21bf10031
Optimize/gallery UI (#7308)
* update gallery ui

* update group by setter

* rebase

* fix: ui

* fix: ui

* feat: optimzie code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.7>
2025-01-13 09:58:10 +08:00
llj
55afd3689c
[system admin - departments] fixup & improvements (#7350)
- improved the UX for 'member item'
    - added paginator for the member list
    - fixed sorting members by 'role'
    - fixed member name link
    - added confirm dialog for 'delete member'
    - fixed displaying error msgs for adding multiple members
    - removed 'store the sort way in localStorage'
    - removed 'v2' from code & file names
    - cleaned up files. removed files for the previous version, and etc.
    - fixed 'delete repo'
    - improved the scroll of the main content area (make the department
      name bar & the tabs bar fixed)
    - fixup for mobile
2025-01-10 22:03:31 +08:00
杨顺强
e22eb47f2d update sdoc version and sdoc translate 2025-01-10 18:00:58 +08:00
杨国璇
a27e9a49c0
fix: metadata map basic filters (#7347)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-10 17:03:36 +08:00
杨国璇
c45889b854
Merge pull request #7346 from haiwen/fix-metadata-map-view-toolbar
fix: metadata map view toolbar
2025-01-10 15:57:36 +08:00
杨国璇
404f0b8a82 feat: optimize code 2025-01-10 15:51:11 +08:00
Michael An
02055139b5
Add context menu when no file (#7344)
* dirent none view support create folder

* fix permission
2025-01-10 15:48:34 +08:00
杨国璇
b395771d4c fix: metadata map view toolbar 2025-01-10 15:13:48 +08:00
杨顺强
bab5a0f770 optimize code 2025-01-10 15:10:56 +08:00
杨国璇
d06d54946a
Merge pull request #7275 from haiwen/optimize/map_view
Optimize/map view
2025-01-10 14:48:05 +08:00
杨国璇
595487eeb0
Merge pull request #7337 from haiwen/fix-search-repo-change
fix: search repo change
2025-01-10 14:47:49 +08:00
杨国璇
13efff8eb0 fix: ui 2025-01-10 14:02:03 +08:00
杨顺强
733bb314ba update sdoc version and sdoc translate 2025-01-10 11:29:23 +08:00
杨国璇
125ce2430c feat: optimize code 2025-01-09 17:45:44 +08:00
杨国璇
ed7f501d4d feat: optimize code 2025-01-09 17:42:29 +08:00
杨国璇
e63fd15785 feat: optimize code 2025-01-09 16:28:50 +08:00
JoinTyang
7b7e4cf61a
fix diagram type (#7342) 2025-01-09 10:55:55 +08:00
JoinTyang
d6e0bd29d2
add diagram file type (#7341) 2025-01-09 10:40:15 +08:00
Michael An
f7f69aa910
Remove search single page (#7339)
* 01 remove jquery-ui css

* 02 remove search page

* 03 remove pubuser_search
2025-01-09 10:29:30 +08:00
杨国璇
6133db43a0 fix: reback 2025-01-08 16:46:55 +08:00
杨国璇
d9c50d2568 fix: search repo change 2025-01-08 16:12:22 +08:00
lian
14e56ee8b9
show name when org set admin (#7335) 2025-01-08 15:58:50 +08:00
Michael An
d1e0347194
Fix load departments in not Pro (#7333)
* 01 fix only isPro load departments

* 02 fix transfer repo
2025-01-08 15:58:05 +08:00
Michael An
5a8c9149b9
Fix sys admin in no Pro version (#7336)
* fix repo history is less than 0

* fix setting button icon is green

* fix admin statistic time lang

* fix statistics traffic list when change page
2025-01-08 15:57:38 +08:00
杨国璇
e8f261e0a9 feat: optimize code 2025-01-08 15:10:50 +08:00
Jerry Ren
cd9780dac1
fix(tag): init self links if not exist (#7334) 2025-01-08 14:40:57 +08:00
杨国璇
ff44d97405 fix: bug 2025-01-08 14:40:28 +08:00
杨顺强
606e41a5ce update sdoc version and sdoc translate 2025-01-08 13:42:22 +08:00
Michael An
39328ea23f
change search UI (#7331) 2025-01-08 10:54:05 +08:00
zhouwenxuan
89b5100102 optimize 2025-01-08 09:31:35 +08:00
zhouwenxuan
86d5d564da show cluster photos 2025-01-08 09:31:35 +08:00
zhouwenxuan
4d703eb910 add map mode setter 2025-01-08 09:31:35 +08:00
llj
a372b7fc84
[org admin - departments] fixup & improvements (#7330)
- improved the UX for 'member item'
- added confirm dialog for 'delete member'
- fixed API & displaying error msgs for adding multiple members
- fixed 'delete repo'
- fixed sorting members ascending by 'role'
- removed 'store the sort way in localStorage'
- removed 'v2' from code & file names
2025-01-07 21:00:45 +08:00
杨国璇
57fc2c6a1b
Merge pull request #7329 from haiwen/fix-metadata-status-bug
fix: metadata tags status bug
2025-01-07 18:39:44 +08:00
杨国璇
aacd5da1b4 fix: metadata tags status bug 2025-01-07 18:32:53 +08:00
杨国璇
7e7b0dbd8f
fix: metadata status (#7328)
* fix: metadata status

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-07 18:15:51 +08:00
Michael An
206ecec2aa
Fix get wiki page id (#7322)
* fix get wiki page link

* add test workflow

* add unit for wiki utils function
2025-01-07 17:33:50 +08:00
Michael An
4b33271e5b
fix sys-admin transfer group (#7326) 2025-01-07 17:33:09 +08:00
Jerry Ren
50b6acbcfb
refactor(tag): remove tag name from tag files table (#7325) 2025-01-07 16:10:00 +08:00
杨顺强
9bedcc120a update sdoc version and sdoc translate 2025-01-07 15:35:13 +08:00
杨顺强
b69821357c fix sdoc translate bug 2025-01-07 15:11:06 +08:00
Michael An
b4e56ce006
update translation (#7324) 2025-01-07 14:59:49 +08:00
Michael An
5bd42ddb67
Add draw file icon (#7323) 2025-01-07 14:24:47 +08:00
Michael An
c260d7f443
Fix metadata text (#7321)
* 01 fix metadata long text

* 02 no sub tag

* 03 new view

* update translation
2025-01-07 12:19:20 +08:00
Jerry Ren
2cc16bdf11
feat(tag): display tags with table (#7311) 2025-01-07 12:17:57 +08:00
杨顺强
0c0f07014b update sdoc version and sdoc translate 2025-01-07 11:31:06 +08:00
awu0403
100ec123d5
fix publish wiki (#7318)
* fix publish wiki

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-01-06 18:03:03 +08:00
杨国璇
a76e2064b6
feat: optimize table context menu (#7315)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-06 17:21:50 +08:00
Michael An
dc99e8514c
fix wiki open first page (#7317) 2025-01-06 17:19:50 +08:00
Michael An
b848703ada
fix Storage Backend style (#7319) 2025-01-06 17:19:03 +08:00
杨顺强
b4cda98556 update sdoc version and sdoc translate 2025-01-06 16:55:28 +08:00
skywalker
14790ecd36 fix ENABLE_SEAFDAV 2025-01-06 15:01:37 +08:00
Michael An
b1d74560c0
fix transfer repos func name (#7316) 2025-01-06 14:57:21 +08:00
Michael An
4b4bfb83da
change checkbox default style (#7298)
* change checkbox style

* change checkbox color to blue

* use default checkbox
2025-01-06 14:18:09 +08:00
杨国璇
89924320cb
fix: gallery detail (#7314)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-06 13:46:16 +08:00
Aries
f6a40c778c
update ui (#7313)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2025-01-06 11:12:40 +08:00
Michael An
9f52046a66
fix WebDAV text (#7312) 2025-01-06 10:25:59 +08:00
杨顺强
a6f967d597 update sdoc version and sdoc translate 2025-01-06 10:20:47 +08:00
llj
8b579d5bac
Org admin departments redesign (#7310)
* [org admin] copied 'departments-v2' from 'system admin'

* [org admin] Departments: redesigned it (added a tree panel to display all the departments, and etc.)

* [org admin] Departments: removed unused files(files for the previous one)
2025-01-05 13:37:46 +08:00
Ranjiwei
2733211cc9
Remove codes (#7309)
* remove-seafile-collab-server

* remove-enable-seafle-doc

* update
2025-01-04 18:03:00 +08:00
杨顺强
f0ee34399b
optimize code (#7307) 2025-01-04 15:10:16 +08:00
Michael An
015ec7a2d8
fix publish wiki can drag page (#7305) 2025-01-04 15:03:51 +08:00
杨顺强
4088771757
optimize code (#7306) 2025-01-04 14:45:10 +08:00
杨国璇
1a42eafbe6
feat: tags filter (#7296)
* feat: tags filter

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-04 12:53:41 +08:00
Michael An
e6ef8d76e4
update translation (#7303) 2025-01-04 11:54:33 +08:00
杨顺强
28bb333364
Fix wiki module bug (#7304)
* optimize code

* update sdoc version and sdoc translate
2025-01-04 11:52:54 +08:00
杨顺强
dd177f450c update sdoc version and sdoc translate 2025-01-04 10:17:32 +08:00
杨顺强
27a881a020
optimize code (#7302) 2025-01-04 10:00:04 +08:00
Guodong SU
155d303a12
Add tldraw file editor (#7273)
* add tldr file type

* add tldraw editor render

* optimize code

* add save mode

* update version

* update dependence

* update dependence

* optimize code

* optimize code

---------

Co-authored-by: 杨顺强 <978987373@qq.com>
2025-01-04 09:40:56 +08:00
Michael An
0504d5b03a
change wiki page URL (#7295) 2025-01-03 22:40:35 +08:00
Aries
db287ccf9d
show people name in path (#7281)
* show people name in path

* optimize

* optimize

* optimize

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-03 15:11:06 +08:00
cir9no
3795c054ea
feat: add wiki title search (#7264) 2025-01-03 13:58:32 +08:00
JoinTyang
d5acdb8653
fix seadoc filename search (#7300) 2025-01-03 12:04:02 +08:00
Michael An
b7219ac7f4
fix item dropdown click (#7299) 2025-01-03 10:15:46 +08:00
Michael An
af8dd37f97
change code format (#7297) 2025-01-02 18:24:56 +08:00
杨国璇
d263e1018f
fix: metadata set root (#7293)
* fix: metadata set root

* feat: update code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-02 16:04:46 +08:00
杨顺强
e34b0768ba update sdoc version and sdoc translate 2025-01-02 14:51:19 +08:00
shenzheng-1
f3a68445e0
sdoc ai writing (#7291)
Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2025-01-02 14:46:41 +08:00
Ranjiwei
bcfcfe2578
Update utils.py (#7294) 2025-01-02 12:12:45 +08:00
Ranjiwei
037c3f3a3d
Multi office suite (#7127)
* initiate

* update

* test

* Update file.py

* update

* update

* update

* optimize fronted

* optimize

* update

* update

* update

* Update test_utils.py

* update

* remove-useless-code

* Update repo_office_suite.py

* optimize ui and sql

* optimize

* update

* add is pro version for api

* Update settings.py

* update

* Update models.py

* add-repo-owner-validation

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-01-02 11:22:25 +08:00
Michael An
1b89e8e2f1
fix org add member multiple (#7286) 2025-01-02 10:29:53 +08:00
Michael An
9c1dd6d8a5
fix translation (#7292) 2025-01-02 10:29:15 +08:00
lian
59563965f4 update migrations 2024-12-31 16:37:36 +08:00
杨顺强
5164284763 update sdoc version and sdoc translate 2024-12-31 15:25:55 +08:00
杨顺强
6344fffe9e update sdoc version and sdoc translate 2024-12-31 14:23:57 +08:00
杨国璇
01509b076e
fix: search metadata bug (#7288)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-31 14:02:02 +08:00
JoinTyang
e6008aacf6
can search without keywords (#7290) 2024-12-31 14:00:40 +08:00
feiniks
bcde649294
Multi-storage back-end support for repeated migrations (#7287)
Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2024-12-31 12:12:50 +08:00
Michael An
888c7c28a8
fix metadata i18n (#7285) 2024-12-31 10:48:22 +08:00
llj
7bd6ad587d
[i18n] improved some string(for deleting wiki page) (#7284) 2024-12-30 19:02:32 +08:00
杨国璇
50887d21dc
feat: storage tree-panel section state (#7282)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-30 19:01:33 +08:00
杨国璇
324d981420
feat: metadata insert property permission bug (#7283)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-30 19:00:50 +08:00
杨顺强
5d5f2c31f0 update sdoc version and sdoc translate 2024-12-30 16:18:31 +08:00
杨国璇
ac862f1c0d
feat: metadata view data popover ui (#7280)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-30 15:33:27 +08:00
skywalker
2652a0c389 init mysql connection close 2024-12-30 15:05:22 +08:00
Michael An
8fabf1eee4
change grid item cursor style (#7279) 2024-12-30 11:44:01 +08:00
杨国璇
2ec770a90e
fix: ai tip text (#7276)
Co-authored-by: 杨国璇 <ygx@192.168.1.8>
2024-12-29 15:41:23 +08:00
Michael An
ca42f80d57
change repo API token permission select (#7250) 2024-12-27 21:53:08 +08:00
Huang Junxiang
b83e272f94
Fix background task no logs in failure (#7267)
* fix: background task no logs in failure status

* fix: background task no logs in failure status

* fix: background task no logs in failure status
2024-12-27 20:48:13 +08:00
llj
1058cf2151
Readonly misc fixup (#7274)
* ['dir view'] fixup for the tree panel: don't display 'New Folder', 'New File' in the menu for library shared with 'r' permission

* ['dir view'] don't display empty 'right icons' when hover on the tree nodes in the tree panel

* [codemirror] improved the configuration for the 'readOnly' mode(Don't display cursor for readOnly files)

* fixed eslint warnings
2024-12-27 20:46:35 +08:00
awu0403
76695e5c90
fix (#7228)
* fix

* fix-format

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2024-12-27 17:55:41 +08:00
杨国璇
9d63b3cbfc
Feat ai op tip (#7270)
* feat: ai op tip

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-27 17:45:28 +08:00
Michael An
58e9d1423f
Change modal header button style (#7271)
* 01 remove useless package

* 02 change modal header icons
2024-12-27 17:44:13 +08:00
Michael An
df825cdc6b
fix custom wiki URL (#7272) 2024-12-27 17:42:57 +08:00
Ranjiwei
ecb9cb2b98
Merge pull request #7253 from haiwen/org-month-traffic
show traffic on org admin page
2024-12-27 16:13:38 +08:00
r350178982
96219ccfaa Update org-info.js 2024-12-27 16:11:13 +08:00
杨国璇
c5bafecad3
fix: after delete people photo, can not remove (#7269)
* fix: after delete people photo, can not remove

* feat: add translate

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-27 14:11:39 +08:00
awu0403
10ae44f884
update component log level (#7252)
* update component log level

* update

* Update settings.py

* Update settings.py

* Update settings.py

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2024-12-27 12:15:07 +08:00
lian
24d9ce03f6 update test 2024-12-27 12:12:40 +08:00
lian
0b60e30dbe org admin show traffic 2024-12-27 12:04:25 +08:00
杨国璇
bef98588f7
Feat metadata invalid property (#7268)
* feat: metadata details display invalid property

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-27 11:24:07 +08:00
杨国璇
d85dcea40e
fix: admin share ui (#7265)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-27 11:22:42 +08:00
llj
bd6a35c374
[sort repos] added 'sort by name/size/time' to the table header in 'Files' page; added 'sort by size' to the sort menu (#7263) 2024-12-26 18:04:32 +08:00
杨国璇
c807d09819
fix: image context path (#7262)
* fix: image context path

* feat: update code

* feat: optimize code style

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-26 17:25:46 +08:00
杨国璇
69541fae7e
Merge pull request #7261 from haiwen/feat-search-inpt
feat: search input
2024-12-26 15:58:09 +08:00
杨国璇
e8e55cb687 feat: optimize code 2024-12-26 15:50:42 +08:00
杨国璇
1bf3350c8c feat: search input 2024-12-26 15:30:23 +08:00
llj
7b87b755a0 [wiki] fixed eslint warning 2024-12-26 14:54:13 +08:00
杨国璇
066cf942d3
Merge pull request #7249 from haiwen/fix/kanban_setting_panel_ui
Fix/kanban setting panel UI
2024-12-26 14:23:44 +08:00
杨国璇
a59280997a
fix: dtable ui (#7260)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-26 14:17:45 +08:00
杨国璇
90befdf0a6 fix: bug 2024-12-26 14:17:10 +08:00
zhouwenxuan
ce075f3c4e fix selector ui 2024-12-26 13:54:32 +08:00
zhouwenxuan
9ca75133c4 close detail when open setting panel 2024-12-26 13:54:32 +08:00
llj
aed76073d6
[wiki] added 2 new entrances(one in the wiki card, the other in the w… (#7248)
* [wiki] added 2 new entrances(one in the wiki card, the other in the wiki 'edit' page) for published wikis

* [wiki] fixed eslint warning
2024-12-26 12:28:54 +08:00
杨国璇
4fbdbeb954
fix: metadata add option bug (#7258)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-26 11:14:04 +08:00
杨顺强
b2153af9d7 update sdoc version and sdoc translate 2024-12-26 09:48:10 +08:00
欢乐马
b7b42b5854
update SEAFILE_LOG_TO_STDOUT (#7256) 2024-12-25 22:15:32 +08:00
llj
356fd64d75
[shared dir view] improved 'switch view mode': don't refresh the whole page (#7257) 2024-12-25 20:18:21 +08:00
Aries
57f56a18f1
Feature/filter by tags (#7242)
* filter by tags

* update tag options

* update view filters

* optimize

* optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-12-25 17:29:34 +08:00
杨国璇
dc09360634
fix: gallery params (#7255)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-25 16:36:06 +08:00
awu0403
4f8047f498
System page list wiki (#7195)
* add All wikis

optimize

* update

* optimize

* select wiki info by sql

* optimize publish wiki

* update

* optimize ui

* update

* Update repos.js

* optimize varname

* Update wiki-card-item.js

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2024-12-25 15:43:07 +08:00
杨国璇
9b8b7c9324
feat: file details ai (#7251)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-25 13:53:26 +08:00
shenzheng-1
b139c235f7
show unknown photos (#7246)
* show unknown photos

* update

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-24 16:49:50 +08:00
Aries
336e1fa193
fix bug - move file to searched folder failed (#7247)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-12-24 16:41:46 +08:00
杨顺强
9c06206584 update sdoc version and sdoc translate 2024-12-24 15:13:34 +08:00
Michael An
08abceb14b
custom modal header close icon (#7240)
* seahub custom modal header

* add custom modal header

* special modal use custom close
2024-12-24 11:20:40 +08:00
杨国璇
bbf567562e
feat: update image-lightbox version to 3.0.4 (#7245)
* feat: update image-lightbox version to 3.0.4

* fix: bug

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-24 10:35:56 +08:00
杨顺强
aac29a7d71
fix delete title in middle bug (#7244) 2024-12-24 10:05:25 +08:00
Jerry Ren
46096dca8a
fix(tag-links): change parameters via update tag links (#7243) 2024-12-24 10:04:08 +08:00
杨国璇
8dd627bf1f
feat: deleted repos ui (#7241)
* feat: deleted repos ui

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-23 22:26:11 +08:00
shenzheng-1
5cf3029030
delete remove photo toaster (#7239)
Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-23 18:20:37 +08:00
Aries
2af78338bf
record the status of detail to local storage (#7194)
* record the status of detail to local storage

* face recognition view support show detail

* feat: optimize code

* fix: bug

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-23 17:47:20 +08:00
awu0403
544c4edbf2
fix user to admin (#7237)
Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-23 16:56:21 +08:00
llj
6770711901
[paginator] fixed the paginators for 'repo trash' dialog & 'wiki trash' dialog (#7238) 2024-12-23 16:55:06 +08:00
Michael An
350d06068e
change select style (#7236) 2024-12-23 16:50:01 +08:00
Aries
4667c33434
Optimize/drag and drop in kanban (#7216)
* optimize drag and drop placeholder and animation

* update trello-smooth-dnd

* update trello-smooth-dnd

* feat: restore code

* feat: readonly style

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-23 15:52:02 +08:00
杨顺强
db08b8bdc9 update sdoc version and sdoc translate 2024-12-23 14:41:01 +08:00
shenzheng-1
066c43dec9
remove photo (#7208)
* remove photo

* update

* update

* update

* update

* update

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-23 14:28:02 +08:00
Michael An
46f9dfb1bd
Fix transfer dialog wrong text (#7235)
* fix transfer link tip

* update translation
2024-12-23 11:47:27 +08:00
Michael An
1d7521f8ed
change search box click x to clear search value (#7233) 2024-12-23 11:27:20 +08:00
shenzheng-1
3f82f6e72f
update add link (#7234)
* update add link

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-23 11:03:41 +08:00
杨国璇
6247a27ef6
feat: image light ocr (#7224)
* feat: image light ocr

* feat: optimize code

* feat: update code

* feat: update imagelight version

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-23 11:02:39 +08:00
llj
49bd39f7df
Repo history redesign (#7229)
* [repo history] display it with a dialog instead of an independent page

* [repo history] redesigned the 'commit details' dialog

* [repo history] added prop type checking
2024-12-21 18:22:52 +08:00
杨国璇
9fb6870679
fix: details rename (#7232)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-21 18:22:08 +08:00
Michael An
a50b5d8f5a
fix markdown history style (#7231) 2024-12-21 18:19:32 +08:00
Michael An
274229679a
fix rename wiki history version bug (#7227) 2024-12-21 18:18:31 +08:00
杨顺强
4af42afc89 update sdoc version and sdoc translate 2024-12-21 15:57:23 +08:00
杨顺强
d3f8d512a8 update sdoc version and sdoc translate 2024-12-21 15:49:27 +08:00
杨顺强
895f3ad670
optimize code (#7230) 2024-12-21 15:39:40 +08:00
杨顺强
df69034928 update sdoc version and sdoc translate 2024-12-21 15:14:19 +08:00
shenzheng-1
aa921af380
add translate api (#7196)
* add translate api

* update

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-21 14:26:57 +08:00
llj
a4002c23c1 ['error' page] added the missing image file 2024-12-21 14:26:07 +08:00
Stephen
8ef2c35331
Feature/adjust inserting sdoc file link (#7190)
* insert sdoc file link directly

* adjust search files api
2024-12-21 13:49:25 +08:00
Michael An
bdc09c0e4e
Fix Wiki no history page (#7226)
* fix no page history

* fix wiki aria
2024-12-20 18:27:43 +08:00
Jerry Ren
cb73865b21
feat(tag): support self link (#7225) 2024-12-20 17:59:47 +08:00
Michael An
bde5ec063e
change wiki2 search icon (#7223)
* change wiki2 search icon

* change clear search
2024-12-20 14:38:29 +08:00
Michael An
7eaa2b7fef
update share wiki permission (#7219)
* update permission

* change page permission

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-20 14:37:07 +08:00
杨国璇
3a783dac7c
fix: trash dialog UI (#7221)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-20 11:29:05 +08:00
杨顺强
5512820ecb update sdoc version and sdoc translate 2024-12-20 10:29:43 +08:00
Aries
b27a0170c7
Optimize/metadata context menu (#7206)
* move record

* add copy option in gallery

* optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimzie code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-19 16:21:06 +08:00
Aries
6d17ce3093
open file when space key down on file name cell (#7218)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-12-19 16:20:32 +08:00
awu0403
b3aaba6ea2
add publish wiki config (#7205)
* add publish wiki config

* update test

* update can_create_wiki

* update test

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-19 15:14:45 +08:00
JoinTyang
bd013174a9
update file and wiki index (#7220) 2024-12-19 15:00:57 +08:00
Michael An
e90e9d1658
language select not need clear icon (#7217) 2024-12-19 10:56:18 +08:00
Michael An
3061e1f88d
change search icon style (#7210) 2024-12-19 10:43:12 +08:00
llj
118e041b55
[dialogs] redesigned the side panel of dialogs('share' dialog', 'folder permission' dialog, 'share admin' dialog, 'share wiki' dialog ...) (#7215) 2024-12-19 09:54:41 +08:00
Michael An
fc139a83fd
Change select group UI (#7201)
* 01 share repo to group UI

* 02 ADD_SHARED_REPO_INTO_GROUP

* 03 share folder to groups

* 04 system admin share repo to group

* 05 remove old NoGroupMessage

* 06 change API

* change API

* change select icons indents
2024-12-19 09:53:32 +08:00
Ranjiwei
68f791bb32
Update settings.py (#7213) 2024-12-18 17:15:59 +08:00
杨国璇
08d25a9e1c
Merge pull request #7204 from haiwen/refactor-metadata-op-api
refactor: metadata op api
2024-12-18 15:56:40 +08:00
awu0403
dfd6550c1a
log to stdout (#6834)
* Update settings.py

* fix

* Update settings.py

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-18 15:40:53 +08:00
llj
dd49dc1e41
['error' page] redesigned the 'error' page for 'Unable to view file' (#7214) 2024-12-18 15:25:38 +08:00
杨国璇
a9753d876c
fix: tag duplicate name (#7209)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-18 15:24:03 +08:00
杨国璇
34c415ac1b refactor: metadata op api 2024-12-18 13:38:17 +08:00
杨顺强
a0c928fa88 update sdoc version and sdoc translate 2024-12-18 10:28:11 +08:00
杨国璇
4b4e03f19f
fifx: auto tags ui (#7211)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-17 18:23:19 +08:00
杨顺强
52209a3d71 update sdoc version and sdoc translate 2024-12-17 17:39:04 +08:00
llj
3ef2be6804
[pdf file view] redesigned the 'decrypt document' dialog for encrypted files (#7207) 2024-12-17 16:41:14 +08:00
llj
e645b29d87
Pdf file view redesign (#7188)
* [pdf file view] added 'find' operation icon; redesigned the 'find' bar

* [pdf file view] the 'find' bar: updated the 'find previous/next' buttons; added a 'clear query' function

* [pdf file view] 'print': redesigned the 'Preparing document for printing' dialog

* [pdf file view] the 'find' bar: updated the styles of the border & the 'input'

* [pdf file view] 'preparing document for printing' dialog: updated UI details

* [pdf file view] 'preparing document for printing' dialog: modified the border & the backdrop
2024-12-17 14:45:57 +08:00
awu0403
d3d2335fe2
fix generate git thumbnail (#7174)
* fix generate git thumbnail

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-17 14:45:21 +08:00
杨顺强
616fafc0c4 update sdoc version and sdoc translate 2024-12-17 14:09:28 +08:00
Michael An
759bb6a05f
fix group member select dirent or file UI (#7202) 2024-12-17 14:02:08 +08:00
Jerry Ren
ae0d94e618
feat(metadata-views): support add folder (#7175) 2024-12-16 22:42:39 +08:00
杨国璇
c9c60b7a62
Merge pull request #7203 from haiwen/fix-download-people-image
fix: download people image
2024-12-16 18:42:00 +08:00
杨国璇
0dcc30ec2f feat: add proptypes 2024-12-16 18:37:24 +08:00
杨国璇
8708ab1c61 fix: download people imahe 2024-12-16 18:35:29 +08:00
杨国璇
d19d22c69d
fix: open in new tab (#7200)
* fix: open in new tab

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-16 18:00:12 +08:00
Aries
34cf5beb0d
add context menu to kanban (#7176)
* add context menu to kanban

* optimize code

* fix: download error, rename error, ...

* feat: optimize code

* feat: support download folder

* feat: optimize delete file callback

* feat: op permission

* feat: rename op

* fix: url //

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-16 17:25:23 +08:00
Michael An
757a334112
fix org register page style (#7199) 2024-12-16 16:16:49 +08:00
杨国璇
302e55fc13
feat: update md-editor version (#7198)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-16 14:45:20 +08:00
Michael An
034d756f06
change error message (#7193) 2024-12-16 12:15:11 +08:00
Michael An
156de7fd6d
update translation (#7192)
* update translation

* update translation
2024-12-16 10:24:14 +08:00
lian
f6fb7ce715
weixin connect (#7184)
* weixin connect

* update
2024-12-16 10:10:40 +08:00
Michael An
13c9525df1
fix wiki bug (#7189) 2024-12-16 09:43:40 +08:00
Aries
9219b49d8c
sticky title in kanban (#7172)
* sticky title in kanban

* optimize header

* optimize ui

* update z-index

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.7>
2024-12-14 23:04:19 +08:00
杨国璇
919cd2eff7
feat: debug (#7187)
* feat: debug

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.7>
2024-12-14 19:30:04 +08:00
Aries
cbc84274b1
add delete button to image previwer in gallery (#7170)
* add delete button to image previwer in gallery

* delete the last image and display the first one

* fix bug - delete the last image

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.7>
2024-12-14 19:29:24 +08:00
Michael An
f07dfc65cb
change select UI (#7178) 2024-12-14 13:17:21 +08:00
杨国璇
de925b1e21
Merge pull request #7186 from haiwen/revert-7185-feat-metadata-ui-beta-version
Revert "debug: metadata ui beta version"
2024-12-13 16:56:33 +08:00
杨国璇
1366bc30f9
Revert "debug: metadata ui beta version" 2024-12-13 16:43:34 +08:00
shenzheng-1
2470f6e5a0
doc ocr (#7101)
* doc ocr

* update

* update

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-13 16:41:42 +08:00
杨国璇
36e8f641c2
Merge pull request #7185 from haiwen/feat-metadata-ui-beta-version
feat: metadata ui beta version
2024-12-13 16:35:36 +08:00
杨国璇
3b9f22e79d feat: metadata ui beta version 2024-12-13 16:34:07 +08:00
Ranjiwei
aa1272ac54
update (#7180) 2024-12-13 16:28:22 +08:00
杨顺强
0a92044b14 update sdoc version and sdoc translate 2024-12-13 15:09:06 +08:00
Michael An
bc8d0d5c48
fix transfer repo (#7183) 2024-12-13 14:32:10 +08:00
Michael An
3a11aceb6b
fix my activities filters (#7166)
* fix my activities filters

* update

---------

Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2024-12-13 10:20:11 +08:00
shenzheng-1
977aa7971e
generate doc tags (#7182)
* generate doc tags

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-13 10:16:52 +08:00
杨国璇
d7f11265ad
fix: metadata details date (#7181)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-12 13:28:13 +08:00
Aries
6ae8f5d3c3
upgrade metadata ui component (#7168)
* upgrade metadata ui component

* update @seafile/sf-metadata-ui-component version

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-12-12 13:14:33 +08:00
杨顺强
e87e1a0870 update sdoc version and sdoc translate 2024-12-11 18:04:49 +08:00
yinjianfei-user
981885670d
optimize wiki links (#7167)
* optimize wiki links

* code optimize

* fix inner bug
2024-12-11 18:04:02 +08:00
lian
6599ab8a8d use contact email as webdav username 2024-12-11 16:32:13 +08:00
Daniel Pan
4c231ea2c3
Revert "Download rate limit (#6941)" (#7173)
This reverts commit 7d3646396e.
2024-12-11 15:00:02 +08:00
awu0403
7d3646396e
Download rate limit (#6941)
* add internal download rate limit api

update test case

* Update internal_api.py

* Update internal_api.py

* update quota

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-11 14:49:51 +08:00
杨顺强
c8390efcc3 update sdoc version and sdoc translate 2024-12-11 10:34:22 +08:00
Michael An
fc05e4827d
Change delete wiki page UI (#7171)
* change delete wiki page UI

* optimise codes

* fix text
2024-12-11 10:09:16 +08:00
awu0403
d9c9779327
move user default quota (#7159)
* move user default quota

* update

* optimize code

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-10 21:34:54 +08:00
Michael An
00e8d7b65a
change login page icon (#7163) 2024-12-09 17:16:22 +08:00
杨国璇
ac124d20ba
feat: metadata details settings (#7145)
* feat: metadata details settings

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.6>
2024-12-09 16:56:29 +08:00
杨国璇
3f6c4f3d26
feat: rename face recognition view (#7164)
* feat: rename face recognition view

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-09 14:13:37 +08:00
Michael An
3918b093fa
localStorage store isShowFiles (#7165) 2024-12-09 14:13:03 +08:00
杨国璇
58ef1f285b
feat: people sorts (#7152)
* feat: people sorts

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-09 13:58:13 +08:00
awu0403
7fa74ebda3
add org translation (#7162)
* add org translation

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-09 11:39:45 +08:00
Michael An
ff68c1df64
change org translation (#7161) 2024-12-09 10:58:51 +08:00
杨国璇
5e6d6e860c
fix: encrypted repo metadata (#7157)
Co-authored-by: 杨国璇 <ygx@192.168.1.6>
2024-12-07 17:40:43 +08:00
杨顺强
87a9cd0731 update sdoc version and sdoc translate 2024-12-07 15:46:04 +08:00
杨顺强
f9fa216531 update sdoc version and sdoc translate 2024-12-07 14:21:37 +08:00
JoinTyang
915f2f0053
mobile view sdoc (#7149) 2024-12-07 14:07:20 +08:00
JoinTyang
4bdf546f88
fix get metadata bug (#7156) 2024-12-07 13:37:58 +08:00
杨顺强
300a0a4487 update sdoc version and sdoc translate 2024-12-07 11:45:09 +08:00
杨顺强
c33de6b16b update sdoc version and sdoc translate 2024-12-07 11:40:19 +08:00
杨顺强
81ad75e4ef update sdoc version and sdoc translate 2024-12-07 10:38:26 +08:00
杨顺强
1f5ea9fcf2 update sdoc version and sdoc translate 2024-12-07 09:56:46 +08:00
Ranjiwei
7e2bbdac8e
Remove code of draft (#7154)
* initiate

* update

* update

* Update models.py

* Update settings.py

* update

* update
2024-12-06 21:45:32 +08:00
Ranjiwei
f4d15fb574
update-sharelink-with-new-dl-url (#7155) 2024-12-06 21:42:04 +08:00
杨国璇
b484b45f1a
fix: metadata tags lang (#7153)
* fix: metadata tags lang

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-06 18:24:02 +08:00
shenzheng-1
21c9be56a8
add_deleted_tags_and_lang (#7150)
* add_deleted_tags_and_lang

* update

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-06 17:34:54 +08:00
JoinTyang
cfdc87c54e
optimize copy sdoc image (#7151) 2024-12-06 17:11:57 +08:00
杨顺强
9283c2cb1d update sdoc version and sdoc translate 2024-12-06 17:09:48 +08:00
lian
25351e48f5
update work weixin avatar (#7147) 2024-12-06 15:18:41 +08:00
Michael An
37cfac8813
fix notice translation (#7148) 2024-12-06 15:17:30 +08:00
Michael An
19ea6d7dd7
change join wechat group text (#7146) 2024-12-06 11:21:36 +08:00
Michael An
1c6fbc5c93
fix admin chart option (#7144) 2024-12-05 14:11:52 +08:00
Michael An
becf34fe9c
Update translation (#7143) 2024-12-05 11:45:17 +08:00
awu0403
f344d26841
fix about (#7142)
Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-05 09:55:36 +08:00
Jerry Ren
90764f9520
update group in real time (#7141) 2024-12-04 17:49:42 +08:00
JoinTyang
3a4db4bf70
optimize face cluster (#7069)
* optimize face cluster

* update

* optimize code
2024-12-04 17:13:47 +08:00
杨顺强
2addf8d01f update sdoc version and sdoc translate 2024-12-04 17:01:08 +08:00
llj
a771e1ddb1
['share' dialog] fixup for 'get initial active tab' (#7140) 2024-12-04 16:21:54 +08:00
awu0403
c9f9fe070b
fix system admin create repo (#7116)
* fix system admin create repo

* optimize code

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-12-04 12:14:08 +08:00
杨顺强
89213daeed update sdoc version and sdoc translate 2024-12-04 11:56:30 +08:00
杨国璇
1d117bf647
feat: face view support move (#7137)
* feat: face view support move

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-04 10:05:17 +08:00
llj
44d0c2c3db
[wiki] fixup for 'published wiki pages rendered as blank pages' (#7138)
- caused by 'page title'
2024-12-04 10:01:28 +08:00
杨顺强
c3af1fd214 update sdoc version and sdoc translate 2024-12-03 18:15:20 +08:00
杨顺强
b87c529b2e update sdoc version and sdoc translate 2024-12-03 17:43:53 +08:00
Michael An
10d7082d90
fix get image index bug (#7139) 2024-12-03 17:40:08 +08:00
杨顺强
c60dcf817a update sdoc version and sdoc translate 2024-12-03 14:26:13 +08:00
lian
453a17e51d
update weixin/work_weixin/dingtalk sso (#7136)
use request.session.get
2024-12-03 14:15:39 +08:00
cir9no
39f06ce855
feat(wiki): add wiki es search sup (#7087)
* feat(wiki): add wiki es search sup

* chore: optimize wiki check

* chore: fix index_local path

* fix: fix unit test
2024-12-03 13:39:38 +08:00
llj
5f34ff95ea
[wiki side panel] redesigned it (added 'Pages' & 'Other' header bars) (#7134) 2024-12-02 21:47:14 +08:00
Michael An
faf213856e
fix delete all departements (#7135) 2024-12-02 21:46:24 +08:00
杨国璇
54a18ed4ea
feat: metadata support ctrl+x、ctrl+z、ctrl+shift+z (#7132)
* feat: metadata support ctrl+x、ctrl+z、ctrl+shift+z

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-02 21:45:40 +08:00
杨国璇
9a581fde52
fix: md info (#7133)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-12-02 17:53:28 +08:00
shenzheng-1
fd5d998044
image tags (#7124)
* image tags

* update

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-12-02 16:42:22 +08:00
欢乐马
49cd661163
UserActivity remove ForeignKey (#7125)
* UserActivity remove ForeignKey

* UserActivity clean_db_records
2024-12-02 13:50:43 +08:00
Michael An
6a514d87eb
fix create Wiki new page in root (#7131) 2024-12-02 10:38:12 +08:00
杨顺强
b831b25263 update sdoc version and sdoc translate 2024-12-02 10:31:44 +08:00
Michael An
d63b68e8ab
Wiki add new page below or above (#7118)
* Wiki add new page below or above

* change var name

* remove sibling page

* update create wiki page logic

* fix page does not exist

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-11-29 22:03:59 +08:00
llj
51d5706aa6
[size unit] changed 'bytes' to 'B' (#7128) 2024-11-29 17:41:35 +08:00
llj
429a2f39d3
[sdoc history page] changed the direction icons in the 'History Versions' side panel (#7130) 2024-11-29 17:41:10 +08:00
Michael An
8ba060bcdd
remove 48x48 library icons (#7126) 2024-11-29 14:42:08 +08:00
llj
9048035920
['view mode'] changed the shortcuts to solve the "input !@" problems (#7117)
- It used 'shift + 1' & 'shift + 2' as shortcuts. That caused problems.
  when a user input '!' or '@' in an input on the page, the 'view mode'
  was changed.
- solution: replaced 'shift + 1/2' to 'ctrl/cmd + shift + 1/2'
2024-11-29 11:29:56 +08:00
Ranjiwei
61fd67b8bb
Merge pull request #7079 from haiwen/admin-audit-log-update
update
2024-11-29 11:23:12 +08:00
杨顺强
2223561985 update seafile editor version 2024-11-29 10:42:57 +08:00
杨国璇
7d206bc691
feat: detail support tag (#7119)
* feat: detail support tag

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-29 09:53:51 +08:00
杨国璇
af0ad3e81c
feat: metadata all tags scroll (#7115)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-28 20:47:38 +08:00
llj
19257fb177
[system admin] 'Settings' page: changed titles of all the setting items to natural English strings, and offered i18n (#7123) 2024-11-28 20:46:22 +08:00
Michael An
b91d2404b7
Fix webp image preview (#7121)
* 01 webp image can preview

* 02 search result page can preview webp

* 03 format codes
2024-11-28 20:44:27 +08:00
杨顺强
0e546a91dd update seafile editor translate 2024-11-28 18:11:44 +08:00
杨顺强
acf730283a update seafile-editor version 2024-11-28 18:10:53 +08:00
Michael An
d6256c7bed
change i18n steps (#7122) 2024-11-28 17:42:58 +08:00
杨顺强
7b7fb111fd update sdoc version and sdoc translate 2024-11-28 16:30:13 +08:00
llj
1a74f9dabf
['wiki' page] redesigned the 'change cover' button (#7120) 2024-11-28 16:05:28 +08:00
杨顺强
ab8d16e2f8 update sdoc version and sdoc translate 2024-11-27 17:19:13 +08:00
杨顺强
72816df9cd update sdoc version and sdoc translate 2024-11-27 16:41:31 +08:00
Michael An
07e75b3900
fix i18n link (#7114) 2024-11-27 12:14:24 +08:00
杨国璇
0ba3a66ed5
fix: metadata refresh display (#7113)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-27 12:06:30 +08:00
杨国璇
7ad7f1d9c6
feat: all tags click (#7112)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-27 11:38:03 +08:00
llj
dce7f1ac98
Lib settings fixup (#7109)
* [lib settings] fixup

* [lib settings] related update(removed unused code & etc.)
2024-11-27 11:18:06 +08:00
Michael An
8edd4c8505
Change grey color (#7091)
* 01 change 818a91 to 666

* 02 change 8c8c8c to 666

* 03 remove useless dtable-icon

* 04 remove dtable-db

* 05 change 888 to 666
2024-11-27 11:03:59 +08:00
杨国璇
ed62007228
feat: refresh metadata view (#7110)
* feat: refresh metadata view

* feat: optimize code

* feat: optimize tip

---------

Co-authored-by: 杨国璇 <ygx@192.168.1.2>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-27 10:59:39 +08:00
Michael An
6f8389e70c
fix translation (#7111) 2024-11-27 09:45:40 +08:00
llj
ffc75b77c3
['dir view' page] don't display the 'views' module for group members when the 'metadata' feature is not turned on (#7106) 2024-11-26 17:50:42 +08:00
杨国璇
b083ecad29
feat: tags keydown support(ctrl+c, delete, drag) (#7107)
* feat: tags keydown support(ctrl+c, delete, drag)

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-26 17:45:58 +08:00
JoinTyang
4aa91aebc6
fix search files bug (#7108) 2024-11-26 17:06:12 +08:00
Michael An
90b668258d
fix library setting dialog highlight tab (#7104) 2024-11-26 16:19:27 +08:00
欢乐马
6543dd3bb8
remove validate_mysql_user_host (#7105) 2024-11-26 16:02:33 +08:00
杨顺强
0d8e198ab1 update sdoc version and sdoc translate 2024-11-26 14:29:00 +08:00
杨国璇
ece36d36f5
fix: metadata load tip (#7103)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-26 12:25:53 +08:00
杨国璇
03fd3b8f55
feat: all tags (#7099)
* feat: all tags

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-25 20:30:46 +08:00
Michael An
d407c9fd63
change file icons (#7100)
* change file icons

* detail panel use big icon
2024-11-25 20:19:44 +08:00
杨顺强
4093c27ed7
Fixed the bug that the lock button is displayed only when the page is refreshed (#7102) 2024-11-25 18:13:08 +08:00
r350178982
224a6c4373 Update internal_api.py 2024-11-25 17:25:52 +08:00
杨顺强
c16e560ff7 update sdoc version and sdoc translate 2024-11-25 17:19:47 +08:00
JoinTyang
d28285babc
sdoc upload video (#7041) 2024-11-25 17:15:06 +08:00
yinjianfei-user
78b829834b
Add wiki history page (#7061)
* add wiki history page

* update button color

* update wiki history page

* code optimize

* code optimize

* update style

* optimize code

* remove wiki-history-page

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-11-25 16:24:30 +08:00
Michael An
4da1b47664
Add scan Wechat qrcode to join group (#7098)
* 01 frontend support wechat entry

* 02 change backend settings

* 03 update icon

* 04 change wechat icon
2024-11-25 14:00:52 +08:00
llj
bacc9e2aa5
['libs' pages] added 'view mode' selector & 'sort' menu for 'my (#7096)
libs, shared with me, shared with all, group, department' pages

- besides that, fixed bugs & improved many lines of code
2024-11-25 10:23:42 +08:00
杨国璇
e7a4e29239
fix: tag files ui (#7095)
* fix: tag files ui

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize ui

* feat: optimize ui

* feat: optimize ui

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.2>
2024-11-24 20:25:52 +08:00
Michael An
4db4711421
fix mobile wiki close side panel (#7094) 2024-11-23 20:33:07 +08:00
yinjianfei-user
ababb20566
fix sdoc detail page style (#7090) 2024-11-23 15:58:11 +08:00
杨顺强
1a6c7e7e89 update sdoc version and sdoc translate 2024-11-23 14:53:15 +08:00
Aries
aea996a5c1
find image index by id in previewer (#7093)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-11-23 14:37:11 +08:00
杨顺强
b0a26cdf92 optimize wiki title style 2024-11-23 14:32:30 +08:00
Michael An
af56e31860
delete wiki page do not jump to first page (#7092) 2024-11-23 13:47:19 +08:00
杨顺强
3643966ee8 update sdoc version and sdoc translate 2024-11-23 11:42:52 +08:00
杨国璇
f0292e5bd3
Merge pull request #7089 from haiwen/fix-tags-load-dir
fix: tags load dir
2024-11-23 10:34:10 +08:00
杨国璇
9db4c52c93 fix: tags load dir 2024-11-23 10:28:46 +08:00
Michael An
187b5df97e
Change wiki nav UI (#7085)
* 02 fix page scroll when move or delete page

* 01 init wiki pages all folded
2024-11-22 22:37:29 +08:00
杨国璇
3a90f736dd
Merge pull request #7088 from haiwen/fix-tags-api
fix: tags api
2024-11-22 18:28:03 +08:00
杨顺强
e022266792 update sdoc version 2024-11-22 18:26:14 +08:00
杨国璇
943d05a545 feat: optimize code 2024-11-22 18:23:49 +08:00
杨国璇
8f1c97ff6d fix: tags api 2024-11-22 18:12:55 +08:00
杨顺强
e8a3812812 update sdoc version and sdoc translate 2024-11-22 17:37:45 +08:00
杨国璇
25860d1ab8
feat: tags (#7029)
* feat: tags

* feat: optimize code

* feat: optimize ui

* feat: update title

* feat: update title

* Feat: tags status management

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize rebase

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.2>
2024-11-22 17:11:55 +08:00
杨国璇
0f6911bf50
feat: metadata file rate (#7053)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-22 16:45:55 +08:00
杨国璇
1dc572d86e
fix: single select bug (#7081)
* fix: single select bug

* feat: optimize css

* fix: multiple cp create option

* fix: people image order

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-22 16:16:50 +08:00
Aries
24e25248c0
update metadata loading ui (#7080)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-11-22 16:15:48 +08:00
Aries
c11c990e1d
update padding in properties side panel (#7083)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-11-22 16:15:11 +08:00
杨顺强
2ff5e91f98
optimize code (#7084) 2024-11-22 15:50:53 +08:00
杨顺强
0317bb2f54 update seafile-editor version 2024-11-21 18:15:16 +08:00
杨国璇
168e0569fa
fix: metadata view rename (#7078)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-21 17:26:52 +08:00
Michael An
a770fad73d
change background orange color (#7076) 2024-11-21 17:26:08 +08:00
r350178982
6199763e6f update 2024-11-21 16:53:41 +08:00
欢乐马
be62042e0e
set_env_config add default config (#7067) 2024-11-21 16:31:22 +08:00
Aries
8535af4b7e
hide capture information ui when value is empty (#7077)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-11-21 15:56:57 +08:00
Aries
4e5c0938fa
show cards quantity in kanban (#7075)
Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-11-21 12:27:13 +08:00
awu0403
ddf3f91bbc
add pdfplumber (#7055)
* add pdfplumber

* Update requirements.txt

* Update utils.py

* optimize code

* optimize

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2024-11-21 11:06:33 +08:00
feiniks
9c178886ef
Support gc online (#7074)
* Support gc online

* Delete check-db-type.py

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2024-11-21 10:33:08 +08:00
shenzheng-1
a207f33e60
revise close face recognition param (#7072)
Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-11-21 07:54:57 +08:00
Michael An
5d31590e9b
fix some wrong text (#7070)
* fix some wrong text

* fix some wrong text
2024-11-20 20:37:46 +08:00
Michael An
f07f357a86
change 'in the menu bae' to 'in the dropdown menu' (#7073) 2024-11-20 20:36:24 +08:00
杨顺强
2edba35cf2 update sdoc version and sdoc translate 2024-11-20 18:00:42 +08:00
欢乐马
2494d33073
fix seafile-ce start (#7071) 2024-11-20 17:42:15 +08:00
杨顺强
d73b7d836c update sdoc version and sdoc translate 2024-11-20 17:24:28 +08:00
Michael An
22cdda0e35
update translation use python3.8 evn (#7068) 2024-11-20 14:43:31 +08:00
lian
b09c520dff Merge branch '11.0' 2024-11-20 14:20:13 +08:00
杨顺强
5151c6e82e update sdoc version and sdoc translate 2024-11-20 14:16:09 +08:00
杨顺强
7f507f44ab update sdoc version and sdoc translate 2024-11-20 14:03:31 +08:00
杨顺强
a452a5990d update sdoc version and sdoc translate 2024-11-20 11:21:37 +08:00
shenzheng-1
84928d6701
desc_support_heic (#7065)
Co-authored-by: zheng.shen <zheng.shen@seafile.com>
2024-11-20 11:10:56 +08:00
Ranjiwei
717f5d21f1
Update test_view_lib_file.py (#7066) 2024-11-20 10:51:35 +08:00
Michael An
5f6de21b19
Change department UI (#7063)
* 01 change department member more icon

* 02 when no contact email return None

* 03 delete menu

* 04 move into member high change

* 05 current department support more operations

* 06 change comment

* add username_as_email is true

* update-get-group-members-info

* Revert "06 change comment"

This reverts commit 14800df3fd.

---------

Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
2024-11-20 10:21:32 +08:00
欢乐马
a004e65b10
fix SEAFDAV_CONF (#7062)
* fix SEAFDAV_CONF

* rm env SEAFES_INDEX_LOGFILE
2024-11-19 18:05:17 +08:00
Aries
d06667462e
optimize kanban (#7054)
* optimize kanban

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-19 16:08:42 +08:00
Michael An
1429051186
change select editor custom style (#7060)
* change select editor custom style

* change add permission
2024-11-19 14:03:58 +08:00
Michael An
a426c96bd9
change Paginator style (#7056) 2024-11-19 11:23:11 +08:00
Michael An
98769ca5bd
change unpublish Wiki (#7059) 2024-11-19 11:22:10 +08:00
Aries
e999be4320
Feature/update copy dialog (#7047)
* update copy dirent dialog

* optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
2024-11-18 21:21:33 +08:00
杨国璇
2531c48f2d
fix: map view position (#7057)
* fix: map view position

* fix: bug

* fix: bug

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-18 18:18:38 +08:00
Aries
3b384be610
Feature/add map view (#7034)
* add map

* use custom baidu js

* use custom baidu plugin

* optimize user location

* optimize code

* show tips when request user location failed

* optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-18 17:36:49 +08:00
杨国璇
29d2d18a0b
fix: metadata cp single-select (#7051)
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2024-11-18 13:34:03 +08:00
Michael An
559d8c52ca
remove markdown viewer dialog (#7052) 2024-11-18 12:07:03 +08:00
杨顺强
9c3e828bf6 update sdoc version and sdoc translate 2024-11-16 16:21:49 +08:00
Michael An
f40ea0540c
change system admin department UI (#7010) 2024-11-15 20:50:16 +08:00
Ranjiwei
d4fc55b76d
intiate (#7049) 2024-11-15 20:38:27 +08:00
llj
edaf88f6cc
[seafileAPI] transfered all the APIs left in seafile-js to seahub; uninstalled seafile-js (#7048) 2024-11-15 20:35:44 +08:00
杨国璇
95d99e10fd
Merge pull request #7042 from haiwen/feature/optimize_kanban
Feature/optimize kanban
2024-11-15 16:40:10 +08:00
杨国璇
3fffdf9eac feat: optimize code 2024-11-15 16:35:07 +08:00
杨国璇
4007c0c10d feat: optimize code 2024-11-15 16:31:18 +08:00
杨国璇
5a7e25740f feat: optimize code 2024-11-15 16:08:46 +08:00
llj
3045a7bcd7
[wiki] enable 'back & forward' in the browser to work (#7045)
* [wiki] enable 'back & forward' in the browser to work

* [wiki] back & forward: update
2024-11-15 14:29:25 +08:00
zhouwenxuan
a629d1c07c optimize code 2024-11-15 10:39:08 +08:00
zhouwenxuan
160dc2b447 open file 2024-11-15 10:39:08 +08:00
zhouwenxuan
0b30d895f3 show dirent detail 2024-11-15 10:39:08 +08:00
feiniks
b3da26a2ad
Check container name (#6971) (#6988)
Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2024-11-01 17:09:50 +08:00
1681 changed files with 267215 additions and 129177 deletions

View File

@ -4,12 +4,15 @@ on:
push:
branches:
- master
- "12.0"
- "13.0"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest

View File

@ -6,13 +6,23 @@ on:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
REDIS_HOST: localhost
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
services:
redis:
image: redis:latest
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 3
ports:
- 6379:6379
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.8"
@ -25,6 +35,7 @@ jobs:
sudo apt-get install -y libfuse-dev cmake re2c flex sqlite3
sudo apt-get install -y libssl-dev libsasl2-dev libldap2-dev libonig-dev
sudo apt-get install -y libxml2 libxml2-dev libjwt-dev
sudo apt-get install -y libhiredis-dev
- name: clone and build
run: |
@ -39,9 +50,14 @@ jobs:
pip install -r test-requirements.txt
sudo rm -rf /usr/lib/python3/dist-packages/pytz/
- name: Set REDIS_HOST environment variable
run: |
echo "REDIS_HOST=localhost" >> $GITHUB_ENV
- name: run pytest
run: |
cd $GITHUB_WORKSPACE
rm -r tests/seahub/repo_metadata
export CCNET_CONF_DIR=/tmp/ccnet SEAFILE_CONF_DIR=/tmp/seafile-data TRAVIS=1 SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=seahub
if ./tests/test_seahub_changes.sh; then ./tests/seahubtests.sh init && ./tests/seahubtests.sh runserver && ./tests/seahubtests.sh test; else true; fi
@ -49,7 +65,7 @@ jobs:
with:
node-version: "20.x"
- name: run npm lint
- name: run npm lint and npm test
run: |
cd $GITHUB_WORKSPACE/tests/
if chmod +x test_frontend_changes.sh && ./test_frontend_changes.sh; then chmod +x github_actions_npm_lint.sh && ./github_actions_npm_lint.sh; else true; fi

View File

@ -28,8 +28,8 @@ minimum_perc = 0
resource_name = sdoc-editor
[o:haiwen:p:seahub:r:seafile-editor]
file_filter = media/locales/<lang>/seafile-editor.json
source_file = media/locales/en/seafile-editor.json
file_filter = media/seafile-editor/locales/<lang>/seafile-editor.json
source_file = media/seafile-editor/locales/en/seafile-editor.json
source_lang = en
type = KEYVALUEJSON

View File

@ -52,5 +52,16 @@ Then open your browser, and input `http://localhost:8000/`, there should be a Lo
Internationalization (I18n)
==========
Please refer to https://github.com/haiwen/seafile/wiki/Seahub-Translation
Please submit translations via Transifex:
Steps:
1. Visit the webpage of Transifex ([https://explore.transifex.com/haiwen/seahub/](https://explore.transifex.com/haiwen/seahub/)).
2. Click the "Join this project" button in the bottom right corner.
3. Use an email or GitHub account(recommended) to create an account.
4. Select a language and click 'Join project' to join the language translation.
5. After accepted by the project maintainer, then you can upload your file or translate online.

View File

@ -1,4 +1,4 @@
'use strict';
const path = require('path');
const fs = require('fs');
@ -22,8 +22,10 @@ const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
// );
// reset by custom
const HOST = process.env.HOST || '0.0.0.0';
const PORT = process.env.PORT || '3000';
const CONFIG_HOST = process.env.HOST;
const isRunInDocker = CONFIG_HOST === '0.0.0.0';
const HOST = isRunInDocker ? '127.0.0.1' : CONFIG_HOST;
const PORT = process.env.PORT || '3001';
const publicPath = process.env.PUBLIC_PATH || '/assets/bundles/';
const publicUrlOrPath = `http://${HOST}:${PORT}${publicPath}`;
@ -79,5 +81,4 @@ module.exports = {
};
module.exports.moduleFileExtensions = moduleFileExtensions;

View File

@ -23,7 +23,6 @@ const modules = require('./modules');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
const webpackBundleTracker = require('webpack-bundle-tracker');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ForkTsCheckerWebpackPlugin =
process.env.TSC_COMPILE_ON_ERROR === 'true'
@ -106,6 +105,7 @@ const excludedChunkNames = [
'sharedFileViewMarkdown',
'markdownEditor',
'plainMarkdownEditor',
'tldrawEditor',
];
// This is the production and development configuration.
@ -357,6 +357,7 @@ module.exports = function (webpackEnv) {
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
'@': path.resolve(process.cwd(), 'src'),
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
@ -437,12 +438,17 @@ module.exports = function (webpackEnv) {
ref: true,
},
},
{
loader: require.resolve('file-loader'),
{ loader: 'svgo-loader',
options: {
name: 'static/media/[name].[hash].[ext]',
},
},
plugins: [
'removeTitle',
'removeStyleElement',
'cleanupIDs',
'inlineStyles',
'removeXMLProcInst',
]
}
}
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
@ -595,10 +601,19 @@ module.exports = function (webpackEnv) {
test: /\.svg$/,
use: [
{
loader: 'svg-sprite-loader', options: {}
loader: require.resolve('@svgr/webpack'),
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
},
},
{ loader: 'svgo-loader', options: {
plugins:[
plugins: [
'removeTitle',
'removeStyleElement',
'cleanupIDs',
@ -626,6 +641,12 @@ module.exports = function (webpackEnv) {
// Make sure to add the new loader(s) before the "file" loader.
],
},
{
test: /\.m?js$/,
resolve: {
fullySpecified: false
}
}
].filter(Boolean),
},
plugins: [
@ -828,7 +849,6 @@ module.exports = function (webpackEnv) {
filename: isEnvProduction ? './webpack-stats.pro.json' : './webpack-stats.dev.json',
publicPath: isEnvProduction ? '' : paths.publicUrlOrPath
}),
// new BundleAnalyzerPlugin(),
].filter(Boolean),
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter

View File

@ -1,6 +1,8 @@
const paths = require('./paths');
const entryFiles = {
tldrawEditor: '/tldrawEditor.js',
excalidrawEditor: '/excalidraw-editor.js',
markdownEditor: '/index.js',
plainMarkdownEditor: '/pages/plain-markdown-editor/index.js',
TCAccept: '/tc-accept.js',
@ -23,6 +25,7 @@ const entryFiles = {
sharedFileViewAudio: '/shared-file-view-audio.js',
sharedFileViewDocument: '/shared-file-view-document.js',
sharedFileViewSpreadsheet: '/shared-file-view-spreadsheet.js',
sharedFileViewExdraw: '/shared-file-view-exdraw.js',
sharedFileViewSdoc: '/shared-file-view-sdoc.js',
sharedFileViewUnknown: '/shared-file-view-unknown.js',
historyTrashFileView: '/history-trash-file-view.js',
@ -39,7 +42,6 @@ const entryFiles = {
repoFolderTrash: '/repo-folder-trash.js',
orgAdmin: '/pages/org-admin',
sysAdmin: '/pages/sys-admin',
search: '/pages/search',
uploadLink: '/pages/upload-link',
subscription: '/subscription.js',
institutionAdmin: '/pages/institution-admin/index.js'

17397
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,19 @@
"@codemirror/view": "^6.34.1",
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@gatsbyjs/reach-router": "1.3.9",
"@seafile/react-image-lightbox": "3.0.1",
"@excalidraw/excalidraw": "^0.18.0",
"@gatsbyjs/reach-router": "2.0.1",
"@seafile/react-image-lightbox": "4.0.2",
"@seafile/resumablejs": "1.1.16",
"@seafile/sdoc-editor": "1.0.135",
"@seafile/sdoc-editor": "2.0.54",
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "^1.0.122",
"@seafile/sf-metadata-ui-component": "^0.0.53",
"@seafile/seafile-editor": "2.0.2",
"@seafile/stldraw-editor": "1.0.1",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/codemirror-themes": "^4.23.5",
"@uiw/react-codemirror": "^4.19.4",
"axios": "^1.7.4",
"chart.js": "3.6.0",
"axios": "^1.8.2",
"chart.js": "4.4.7",
"classnames": "^2.2.6",
"codemirror": "^6.0.1",
"copy-to-clipboard": "^3.0.8",
@ -32,24 +33,24 @@
"i18next-xhr-backend": "^3.1.2",
"is-hotkey": "0.2.0",
"MD5": "^1.3.0",
"mdast-util-gfm-autolink-literal": "2.0.0",
"object-assign": "4.1.1",
"prop-types": "^15.8.1",
"qrcode.react": "^1.0.1",
"react": "17.0.2",
"qrcode.react": "4.2.0",
"react": "18.3.1",
"react-app-polyfill": "^2.0.0",
"react-chartjs-2": "4.0.0",
"react-chartjs-2": "5.3.0",
"react-cookies": "^0.1.0",
"react-dnd": "^2.6.0",
"react-dnd-html5-backend": "^2.6.0",
"react-dom": "17.0.2",
"react-dom": "18.3.1",
"react-i18next": "^10.12.2",
"react-responsive": "9.0.2",
"react-select": "5.7.0",
"react-mentions": "4.4.10",
"react-responsive": "10.0.0",
"react-select": "5.9.0",
"react-transition-group": "4.4.5",
"reactstrap": "8.9.0",
"seafile-js": "0.2.237",
"socket.io-client": "^2.2.0",
"svg-sprite-loader": "^6.0.11",
"reactstrap": "9.2.3",
"socket.io-client": "^4.8.1",
"svgo-loader": "^3.0.1",
"unified": "^7.0.0",
"url-parse": "^1.4.3",
@ -63,7 +64,7 @@
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"dev": "export NODE_ENV=development && node config/server.js"
"dev": "export NODE_ENV=development && node --max-old-space-size=4096 config/server.js"
},
"browserslist": {
"production": [
@ -168,7 +169,6 @@
"url-loader": "4.1.1",
"web-vitals": "2.1.4",
"webpack": "^5.64.4",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-bundle-tracker": "1.7.0",
"webpack-dev-server": "^4.6.0",
"webpack-manifest-plugin": "^4.0.2",

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import { Router, navigate } from '@gatsbyjs/reach-router';
import { createRoot } from 'react-dom/client';
import { Router, navigate, LocationProvider, globalHistory } from '@gatsbyjs/reach-router';
import MediaQuery from 'react-responsive';
import { Modal } from 'reactstrap';
import { siteRoot, siteTitle, mediaUrl, faviconPath } from './utils/constants';
@ -8,6 +8,7 @@ import { Utils, isMobile } from './utils/utils';
import SystemNotification from './components/system-notification';
import EventBus from './components/common/event-bus';
import Header from './components/header';
import SystemUserNotification from './components/system-user-notification';
import SidePanel from './components/side-panel';
import ResizeBar from './components/resize-bar';
import {
@ -24,7 +25,7 @@ import ShareAdminLibraries from './pages/share-admin/libraries';
import ShareAdminFolders from './pages/share-admin/folders';
import ShareAdminShareLinks from './pages/share-admin/share-links';
import ShareAdminUploadLinks from './pages/share-admin/upload-links';
import SharedLibraries from './pages/shared-libs/shared-libraries';
import SharedLibraries from './pages/shared-libs';
import ShareWithOCM from './pages/share-with-ocm/shared-with-ocm';
import OCMViaWebdav from './pages/ocm-via-webdav/ocm-via-webdav';
import OCMRepoDir from './pages/share-with-ocm/remote-dir-view';
@ -85,7 +86,7 @@ class App extends Component {
let splitUrlArray = window.location.hash.split('/');
let repoID = splitUrlArray[splitUrlArray.length - 2];
let url = siteRoot + 'library/' + repoID + '/';
navigate(url, { repalce: true });
navigate(url, { replace: true });
}
};
@ -144,7 +145,7 @@ class App extends Component {
if (selectedItem.is_dir === true) {
this.setState({ currentTab: '', pathPrefix: [] });
let url = siteRoot + 'library/' + selectedItem.repo_id + '/' + selectedItem.repo_name + selectedItem.path;
navigate(url, { repalce: true });
navigate(url, { replace: true });
} else {
let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + Utils.encodePath(selectedItem.path);
let isWeChat = Utils.isWeChat();
@ -157,19 +158,6 @@ class App extends Component {
}
};
onGroupChanged = (groupID) => {
setTimeout(function () {
let url;
if (groupID) {
url = siteRoot + 'group/' + groupID + '/';
}
else {
url = siteRoot + 'libraries/';
}
window.location = url.toString();
}, 1);
};
tabItemClick = (tabName, groupID) => {
let pathPrefix = [];
if (groupID || this.dirViewPanels.indexOf(tabName) > -1) {
@ -291,6 +279,7 @@ class App extends Component {
return (
<React.Fragment>
<SystemNotification />
<SystemUserNotification />
<Header
isSidePanelClosed={isSidePanelClosed}
onCloseSidePanel={this.onCloseSidePanel}
@ -343,7 +332,7 @@ class App extends Component {
<InvitationsView path={siteRoot + 'invitations/'} />
<FilesActivities path={siteRoot + 'dashboard'} />
<MyFileActivities path={siteRoot + 'my-activities'} />
<GroupView path={siteRoot + 'group/:groupID'} onGroupChanged={this.onGroupChanged} />
<GroupView path={siteRoot + 'group/:groupID'} />
<LinkedDevices path={siteRoot + 'linked-devices'} />
<ShareAdminLibraries path={siteRoot + 'share-admin-libs'} />
<ShareAdminFolders path={siteRoot + 'share-admin-folders'} />
@ -374,4 +363,9 @@ class App extends Component {
}
}
ReactDom.render(<App />, document.getElementById('wrapper'));
const root = createRoot(document.getElementById('wrapper'));
root.render(
<LocationProvider history={globalHistory}>
<App />
</LocationProvider>
);

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#999999;}
</style>
<title>ai</title>
<g id="ai">
<g id="ai-assistant">
<path id="形状" class="st0" d="M16.1,0C24.9,0,32,6.8,32,15s-7.1,15-15.9,15c-0.1,0-0.3,0-0.6,0c-0.4,0-3.7,0.2-4.9,0.6
c-1,0.2-2.8,0.9-3.9,1.2C6.6,31.9,6.2,32,6,32c-0.5,0-0.9-0.4-1-1c-0.2-1.1-0.2-3,0.4-4.7C2.1,23.5,0,19.6,0,15
C0.1,6.8,7.2,0,16.1,0z M16,3C8.8,3,3,8.6,3,15.4c0,3.5,1.7,6.6,4.5,9.1l1.2,1.1c-0.4,0.8-0.7,1.3-0.8,1.7
c-0.2,0.5-0.3,1.3-0.2,1.7c0.8-0.4,2-1,2.9-1.2c1.7-0.4,4.2-0.6,5.4-0.6c5.7,0,13-5,13-11.8S23.2,3,16,3z M13.1,6.9
C10.3,6.9,8,9.1,8,11.8v10c0,0.1,0.1,0.3,0.3,0.3h2.8V11.9c0-1,0.9-1.9,1.9-1.9s1.9,0.9,1.9,1.9v2.3H12c-0.1,0-0.3,0-0.4,0.1
c-0.1,0.1-0.1,0.3-0.1,0.4v1.9c0,0.4,0.3,0.6,0.6,0.6H15v4.9h2.8c0.1,0,0.3-0.1,0.3-0.3v-10C18.1,9.1,15.9,6.9,13.1,6.9L13.1,6.9z
M22.4,11h-1.8c-0.4,0-0.6,0.2-0.6,0.6v9.8c0,0.4,0.2,0.6,0.6,0.6h1.8c0.4,0,0.6-0.2,0.6-0.6v-9.8C23,11.2,22.8,11,22.4,11z
M21.5,10c0.8,0,1.5-0.7,1.5-1.5S22.3,7,21.5,7S20,7.7,20,8.5S20.7,10,21.5,10z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 683 B

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#999999;}
</style>
<title>filter-circled</title>
<g id="filter-circled">
<path id="形状结合" class="st0" d="M16,1c8.3,0,15,6.7,15,15s-6.7,15-15,15S1,24.3,1,16S7.7,1,16,1z M16,4C9.4,4,4,9.4,4,16
s5.4,12,12,12s12-5.4,12-12S22.6,4,16,4z M20,20c0.6,0,1,0.4,1,1s-0.4,1-1,1h-8c-0.6,0-1-0.4-1-1s0.4-1,1-1H20z M22,15
c0.6,0,1,0.4,1,1s-0.4,1-1,1H10c-0.6,0-1-0.4-1-1s0.4-1,1-1H22z M24,10c0.6,0,1,0.4,1,1s-0.4,1-1,1H8c-0.6,0-1-0.4-1-1s0.4-1,1-1
H24z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 846 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" xmlns="http://www.w3.org/2000/svg" aria-labelledby="title" viewBox="0 0 32 32" preserveAspectRatio="xMidYMid meet" fill="currentColor" width="48" height="48" title="view-back"><title id="title">view-back</title><g><path d="M19.768,23.89c0.354,-0.424 0.296,-1.055 -0.128,-1.408c-1.645,-1.377 -5.465,-4.762 -6.774,-6.482c1.331,-1.749 5.1,-5.085 6.774,-6.482c0.424,-0.353 0.482,-0.984 0.128,-1.408c-0.353,-0.425 -0.984,-0.482 -1.409,-0.128c-1.839,1.532 -5.799,4.993 -7.2,6.964c-0.219,0.312 -0.409,0.664 -0.409,1.054c0,0.39 0.19,0.742 0.409,1.053c1.373,1.932 5.399,5.462 7.2,6.964l0.001,0.001c0.424,0.354 1.055,0.296 1.408,-0.128Z"></path></g></svg>

After

Width:  |  Height:  |  Size: 780 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730788276176" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15486" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M108.8 768h816c38.4 0 67.2 35.2 67.2 80s-28.8 76.8-67.2 80H108.8c-38.4 0-67.2-35.2-67.2-80s32-80 67.2-80zM924.8 256H108.8C70.4 256 41.6 220.8 41.6 176S73.6 96 108.8 96h816C963.2 96 992 131.2 992 176S963.2 256 924.8 256z m-432 176h409.6c38.4 0 67.2 35.2 67.2 80s-28.8 80-67.2 80H492.8c-38.4 0-67.2-35.2-67.2-80s28.8-80 67.2-80z m-195.2 105.6L128 630.4c-28.8 19.2-67.2-6.4-67.2-38.4v-185.6c0-35.2 38.4-54.4 67.2-38.4l169.6 92.8c28.8 12.8 28.8 60.8 0 76.8z" p-id="15487"></path></svg>

After

Width:  |  Height:  |  Size: 815 B

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#999999;}
</style>
<title>map</title>
<g id="map">
<g id="形状结合" transform="translate(1.000000, 1.000000)">
<path class="st0" d="M29.5,6.8C29.8,7,30,7.4,30,7.8v17.6c0,0.5-0.3,1-0.8,1.2l-8.8,3.3c-0.3,0.1-0.6,0.1-0.8,0L10,26.7l-8.3,3.2
c-0.4,0.1-0.8,0.1-1.2-0.1c-0.3-0.2-0.5-0.6-0.5-1V11.2c0-0.5,0.3-1,0.8-1.2l4-1.5c-0.2,1.1-0.2,2.3,0,3.4l-2,0.9v13.4l5.6-2.4
l0-5.4l2.8,3.5v2.7l7.5,2.5v-5.2l2.8-3.5l0,8l5.6-2.2V10.4l-1.9,0.8c0.1-1.1,0.1-2.2-0.2-3.3l3.2-1.2C28.7,6.5,29.1,6.6,29.5,6.8z
M8.3,2.7c3.7-3.6,9.6-3.6,13.3,0c3.3,3.1,3.7,8,1.1,11.7l-6.2,7.7c-0.1,0.1-0.2,0.2-0.3,0.3c-0.9,0.7-2.1,0.6-2.8-0.3l-6.1-7.6
C4.7,10.8,5.2,5.9,8.3,2.7z M19.4,4.8c-2.4-2.2-6.4-2.2-8.8,0c-2.2,2-2.4,5.1-0.7,7.4l4.1,5.5c0.5,0.7,1.5,0.7,2,0l4.1-5.5
C21.8,9.9,21.6,6.8,19.4,4.8z M15,5c2.2,0,4,1.8,4,4c0,1.4-0.8,2.7-2,3.5s-2.8,0.7-4,0s-2-2-2-3.5C11,6.8,12.8,5,15,5z M15,8
c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S15.6,8,15,8z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" xmlns="http://www.w3.org/2000/svg" aria-labelledby="title" viewBox="0 0 32 32" preserveAspectRatio="xMidYMid meet" fill="currentColor" width="48" height="48" title="view-forward"><title id="title">view-forward</title><g><path d="M12.982,23.89c-0.354,-0.424 -0.296,-1.055 0.128,-1.408c1.645,-1.377 5.465,-4.762 6.774,-6.482c-1.331,-1.749 -5.1,-5.085 -6.774,-6.482c-0.424,-0.353 -0.482,-0.984 -0.128,-1.408c0.353,-0.425 0.984,-0.482 1.409,-0.128c1.839,1.532 5.799,4.993 7.2,6.964c0.219,0.312 0.409,0.664 0.409,1.054c0,0.39 -0.19,0.742 -0.409,1.053c-1.373,1.932 -5.399,5.462 -7.2,6.964l-0.001,0.001c-0.424,0.354 -1.055,0.296 -1.408,-0.128Z"></path></g></svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon-rotate</title>
<g id="seafile" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon-rotate" transform="translate(8, 8) scale(-1, 1) translate(-8, -8)translate(-0, 0)">
<rect id="image2" transform="translate(8, 8) scale(-1, 1) translate(-8, -8)" x="0" y="0" width="16" height="16"></rect>
<path d="M13.8526316,16 L4.54736842,16 C3.36370614,16 2.4,15.057073 2.4,13.9003426 L2.4,6.89965737 C2.4,5.74109969 3.36370614,4.8 4.54736842,4.8 L13.8526316,4.8 C15.0362938,4.8 16,5.74292707 16,6.89965737 L16,13.9003426 C16,15.057073 15.0362938,16 13.8526316,16 Z M4.69333334,6.4 C4.31055555,6.4 4,6.69932565 4,7.06652165 L4,13.7334783 C4,14.1006743 4.31055555,14.4 4.69333334,14.4 L13.7066666,14.4 C14.0894445,14.4 14.4,14.1006743 14.4,13.7334783 L14.4,7.06652165 C14.4,6.69932565 14.0894445,6.4 13.7066666,6.4 L4.69333334,6.4 Z" id="image" fill="#666666" fill-rule="nonzero"></path>
<g id="group" transform="translate(4.8173, 3.52) scale(-1, 1) translate(-4.8173, -3.52)translate(0.0347, 0)" fill="#666666" fill-rule="nonzero">
<path d="M8.87810624,7.04 C8.61343976,7.04 8.36129136,6.88219178 8.24862928,6.61795476 C6.78581083,3.19571838 3.28255715,2.68559414 1.32438325,2.68559414 C0.945266472,2.68559414 0.637681159,2.3699777 0.637681159,1.98279707 C0.637681159,1.59561644 0.945266472,1.28 1.32438325,1.28 C5.22642483,1.28 8.20928696,3.02139535 9.50758312,6.0564511 C9.65958752,6.41243708 9.50043,6.8271424 9.1535024,6.98311564 C9.064088,7.02165021 8.9693088,7.04 8.87810624,7.04 Z" id="path2"></path>
<path d="M1.42164886,3.84 C1.29607734,3.84 1.1692245,3.77584105 1.07312386,3.64578912 L0.144150966,2.39035448 C-0.0480503219,2.13025062 -0.0480503219,1.71061639 0.144150966,1.45051253 L1.07312386,0.195077896 C1.26532514,-0.0650259653 1.57669123,-0.0650259653 1.76889251,0.195077896 C1.96109379,0.455181757 1.96109379,0.874815984 1.76889251,1.13491985 L1.18716329,1.9204335 L1.76889251,2.70594717 C1.96109379,2.96605102 1.96109379,3.38568526 1.76889251,3.64578912 C1.67407323,3.77410702 1.54722036,3.84 1.42164886,3.84 Z" id="path1"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1721786317709" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22672" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 304c115.2 0 208 92.8 208 208s-92.8 208-208 208-208-92.8-208-208 92.8-208 208-208z m0 128c-44.8 0-80 35.2-80 80s35.2 80 80 80 80-35.2 80-80-35.2-80-80-80z" p-id="22673"></path><path d="M432 57.6c-9.6-19.2-32-28.8-51.2-22.4-80 19.2-150.4 60.8-208 112-12.8 16-16 35.2-9.6 54.4 6.4 12.8 9.6 25.6 9.6 41.6 0 51.2-41.6 92.8-89.6 92.8-19.2 0-38.4 12.8-41.6 32C22.4 409.6 16 460.8 16 512c0 35.2 3.2 67.2 9.6 99.2 3.2 22.4 25.6 38.4 48 35.2h9.6c51.2 0 89.6 41.6 89.6 92.8 0 22.4-9.6 41.6-22.4 57.6-16 16-12.8 41.6 3.2 60.8 57.6 60.8 131.2 105.6 214.4 131.2 22.4 6.4 48-6.4 54.4-28.8 12.8-38.4 44.8-64 86.4-64s73.6 25.6 86.4 64c6.4 22.4 32 35.2 54.4 28.8 83.2-25.6 156.8-70.4 214.4-131.2 16-16 16-41.6 3.2-60.8-12.8-16-22.4-35.2-22.4-57.6 0-51.2 41.6-92.8 89.6-92.8h9.6c22.4 3.2 44.8-12.8 48-35.2 6.4-32 9.6-67.2 9.6-99.2 0-51.2-6.4-102.4-22.4-150.4-6.4-19.2-22.4-32-41.6-32-51.2 0-89.6-41.6-89.6-92.8 0-16 3.2-28.8 9.6-41.6 9.6-16 3.2-38.4-9.6-51.2-57.6-54.4-128-92.8-204.8-115.2-19.2 0-41.6 9.6-51.2 28.8-16 28.8-44.8 51.2-80 51.2s-67.2-22.4-80-51.2zM288 262.4c0-16-3.2-28.8-6.4-41.6 32-25.6 67.2-44.8 105.6-60.8 28.8 38.4 73.6 60.8 124.8 60.8s96-22.4 124.8-60.8c38.4 12.8 73.6 35.2 105.6 60.8-3.2 12.8-6.4 28.8-6.4 41.6 0 80 57.6 150.4 134.4 163.2 6.4 28.8 9.6 57.6 9.6 86.4 0 16 0 28.8-3.2 44.8-80 9.6-140.8 80-140.8 163.2 0 25.6 6.4 51.2 16 73.6-32 28.8-67.2 51.2-105.6 67.2-28.8-48-80-76.8-137.6-76.8s-108.8 32-137.6 76.8c-38.4-16-73.6-38.4-105.6-67.2 9.6-22.4 16-48 16-73.6 0-83.2-60.8-153.6-140.8-163.2 3.2-12.8 3.2-28.8 3.2-41.6 0-28.8 3.2-57.6 9.6-86.4C230.4 412.8 288 345.6 288 262.4z" p-id="22674"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733301494152" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6527" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 320c105.6 0 192 86.4 192 192s-86.4 192-192 192-192-86.4-192-192 86.4-192 192-192z m0 96c-54.4 0-96 41.6-96 96s41.6 96 96 96 96-41.6 96-96-41.6-96-96-96z" p-id="6528"></path><path d="M432 60.8c-9.6-19.2-32-28.8-54.4-22.4-83.2 19.2-153.6 60.8-214.4 112-12.8 16-16 35.2-9.6 54.4 6.4 12.8 9.6 25.6 9.6 41.6 0 51.2-44.8 92.8-92.8 92.8-19.2 0-38.4 12.8-44.8 32-19.2 41.6-25.6 92.8-25.6 144 0 35.2 3.2 67.2 9.6 99.2 3.2 22.4 25.6 38.4 51.2 35.2h9.6c54.4 0 92.8 41.6 92.8 92.8 0 22.4-9.6 41.6-22.4 57.6-16 16-12.8 41.6 3.2 60.8 60.8 60.8 137.6 105.6 224 131.2 22.4 6.4 51.2-6.4 57.6-28.8 12.8-38.4 48-64 89.6-64s76.8 25.6 89.6 64c6.4 22.4 32 35.2 57.6 28.8 86.4-25.6 163.2-70.4 224-131.2 16-16 16-41.6 3.2-60.8-12.8-16-22.4-35.2-22.4-57.6 0-51.2 44.8-92.8 92.8-92.8h9.6c22.4 3.2 48-12.8 51.2-35.2 6.4-32 9.6-67.2 9.6-99.2 0-51.2-6.4-102.4-22.4-150.4-6.4-19.2-22.4-32-44.8-32-54.4 0-92.8-41.6-92.8-92.8 0-16 3.2-28.8 9.6-41.6 3.2-16-3.2-38.4-16-51.2-60.8-54.4-134.4-92.8-211.2-115.2-19.2 0-44.8 9.6-54.4 28.8-16 28.8-48 51.2-83.2 51.2s-70.4-22.4-83.2-51.2zM262.4 240c0-16-3.2-32-6.4-44.8 35.2-28.8 76.8-48 118.4-67.2 32 41.6 83.2 67.2 140.8 67.2 57.6 0 108.8-25.6 140.8-67.2 44.8 12.8 83.2 38.4 118.4 67.2-3.2 12.8-6.4 32-6.4 44.8 0 86.4 64 166.4 150.4 179.2 6.4 32 9.6 64 9.6 96 0 16 0 32-3.2 48-89.6 9.6-160 86.4-160 179.2 0 28.8 6.4 57.6 19.2 80-35.2 32-76.8 57.6-118.4 73.6-32-51.2-89.6-83.2-153.6-83.2s-121.6 35.2-153.6 83.2c-44.8-16-83.2-41.6-118.4-73.6 6.4-25.6 16-51.2 16-80 0-89.6-67.2-169.6-160-179.2 3.2-12.8 3.2-32 3.2-44.8 0-32 3.2-64 9.6-96 89.6-16 153.6-89.6 153.6-182.4z" p-id="6529"></path></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M13.0123,19.3233l7.07107,-7.07107l-9.40841,-9.40842l-6.84754,-0.297719c-0.0144722,-0.00062923 -0.028965,-0.00062923 -0.0434372,0c-0.275882,0.0119949 -0.489804,0.245365 -0.477809,0.521247l0.297719,6.84754l9.40842,9.40842Zm-10.7052,-16.2125c-0.0359846,-0.827645 0.605783,-1.52776 1.43343,-1.56374c0.0434167,-0.00188768 0.086895,-0.00188768 0.130312,0l7.23616,0.314616l10.3906,10.3906l-8.48528,8.48528l-10.3906,-10.3906l-0.314616,-7.23616Zm5.75544,4.1917c-0.585786,0.585786 -1.53553,0.585786 -2.12132,0c-0.585786,-0.585786 -0.585786,-1.53553 0,-2.12132c0.585786,-0.585786 1.53553,-0.585786 2.12132,0c0.585786,0.585786 0.585786,1.53553 0,2.12132Zm-0.707107,-0.707107c0.195262,-0.195262 0.195262,-0.511845 0,-0.707107c-0.195262,-0.195262 -0.511845,-0.195262 -0.707107,0c-0.195262,0.195262 -0.195262,0.511845 0,0.707107c0.195262,0.195262 0.511845,0.195262 0.707107,0Z" fill="#979797" stroke="none"></path></svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M13.7515136,5.93572645 L8.28976642,0.621080351 C7.879571,0.222593643 7.32333684,-0.000847143079 6.7436592,2.41376939e-06 L2.18654926,2.41376939e-06 C0.979680972,2.41376939e-06 0,0.950120356 0,2.12526321 L0,6.55780208 C0,7.12087054 0.230271291,7.66193631 0.64044202,8.06098482 L6.10527317,13.3776312 C6.95834688,14.2074603 8.34132992,14.2074603 9.19440364,13.3776312 L13.7515136,8.94309205 C14.6047509,8.11299113 14.6047509,6.76682748 13.7515136,5.93672657 L13.7515136,5.93572645 Z M12.1129482,8.06825704 L8.34330379,11.7695888 C7.91766113,12.1834794 7.22778724,12.1834794 6.80214458,11.7695888 L2.11187052,7.05109716 C1.90701239,6.85210202 1.79175754,6.58211021 1.79144107,6.30047085 L1.79144107,2.74606218 C1.79169751,2.46489086 1.9068179,2.19534855 2.11144974,1.99679547 C2.31608157,1.79824239 2.59344239,1.68696248 2.88245014,1.68746078 L6.64338195,1.68746078 C6.93283334,1.68746078 7.20969987,1.79859659 7.41396155,1.99731949 L12.1119801,6.56794622 C12.5379286,6.98234468 12.5379286,7.65385856 12.1119801,8.06825704 L12.1129482,8.06825704 Z" id="image2"></path>
<path d="M5.19151264,2.9941275 C4.08690365,2.9941275 3.19144113,3.86530858 3.19144113,4.93996491 C3.19144113,6.01462124 4.08690365,6.88580232 5.19151264,6.88580232 C6.29612164,6.88580232 7.19158415,6.01462124 7.19158415,4.93996491 C7.19158415,3.86530858 6.29612164,2.9941275 5.19151264,2.9941275 L5.19151264,2.9941275 Z M4.88150156,5.43615345 C4.69675086,5.322975 4.58976949,5.12109806 4.60216374,4.9090349 C4.61455799,4.69697174 4.74437157,4.50819414 4.94111847,4.41611989 C5.13786537,4.32404563 5.37049132,4.34320761 5.54852541,4.4661535 C5.81754925,4.64750572 5.88755949,5.00514843 5.70588358,5.2700074 C5.52420766,5.53486636 5.15787671,5.60921928 4.88250159,5.43712636 L4.88150156,5.43615345 Z" id="image"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<style type="text/css">
.st0{fill:#999999;}
</style>
<title>time</title>
<g id="time">
<path id="形状" class="st0" d="M28,16c0-6.6-5.4-12-12-12S4,9.4,4,16s5.4,12,12,12S28,22.6,28,16 M31,16c0,8.2-6.8,15-15,15
S1,24.2,1,16S7.8,1,16,1S31,7.8,31,16 M25,17.5c0,0.8-0.7,1.5-1.5,1.5h-6c-1.7,0-3-1.3-3-3V8.5C14.5,7.7,15.2,7,16,7
s1.5,0.7,1.5,1.5v6c0,0.8,0.7,1.5,1.5,1.5h4.5C24.3,16,25,16.7,25,17.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 759 B

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import QRCode from 'qrcode.react';
import { QRCodeSVG } from 'qrcode.react';
import { Button, Popover, PopoverBody } from 'reactstrap';
import { gettext } from '../utils/constants';
@ -16,8 +16,7 @@ class ButtonQR extends React.Component {
this.state = {
isPopoverOpen: false
};
this.btnID = 'btn-' + Math.random().toString().substr(2, 5);
this.btn = null;
}
togglePopover = () => {
@ -30,14 +29,16 @@ class ButtonQR extends React.Component {
const { link } = this.props;
const { isPopoverOpen } = this.state;
return (
<div className="ml-2">
<Button outline color="primary" className="btn-icon btn-qr-code-icon sf3-font sf3-font-qr-code" id={this.btnID} onClick={this.togglePopover} type="button"></Button>
<Popover placement="bottom" isOpen={isPopoverOpen} target={this.btnID} toggle={this.togglePopover}>
<PopoverBody>
<QRCode value={link} size={128} />
<p className="m-0 mt-1 text-center" style={{ 'maxWidth': '128px' }}>{gettext('Scan the QR code to view the shared content directly')}</p>
</PopoverBody>
</Popover>
<div className="ml-2" ref={ref => this.btn = ref}>
<Button outline color="primary" className="btn-icon btn-qr-code-icon sf3-font sf3-font-qr-code" onClick={this.togglePopover} type="button"></Button>
{this.btn && (
<Popover placement="bottom" isOpen={isPopoverOpen} target={this.btn} toggle={this.togglePopover}>
<PopoverBody>
<QRCodeSVG value={link} size={128} />
<p className="m-0 mt-1 text-center" style={{ 'maxWidth': '128px' }}>{gettext('Scan the QR code to view the shared content directly')}</p>
</PopoverBody>
</Popover>
)}
</div>
);
}

View File

@ -0,0 +1,7 @@
.sf-centered-loading {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}

View File

@ -0,0 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Loading from '../loading';
import './index.css';
function CenteredLoading(props) {
return (
<div className={classnames('sf-centered-loading', props.className)}>
<Loading />
</div>
);
}
CenteredLoading.propTypes = {
className: PropTypes.string,
};
export default CenteredLoading;

View File

@ -0,0 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types';
class ClickOutside extends React.Component {
isClickedInside = false;
componentDidMount() {
document.addEventListener('mousedown', this.handleDocumentClick);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleDocumentClick);
}
handleDocumentClick = (e) => {
if (this.isClickedInside) {
this.isClickedInside = false;
return;
}
this.props.onClickOutside(e);
};
handleMouseDown = () => {
this.isClickedInside = true;
};
render() {
return React.cloneElement(
React.Children.only(this.props.children), {
onMouseDownCapture: this.handleMouseDown
}
);
}
}
ClickOutside.propTypes = {
children: PropTypes.element.isRequired,
onClickOutside: PropTypes.func.isRequired,
};
export default ClickOutside;

View File

@ -21,7 +21,7 @@
.lds-ripple div {
position: absolute;
border: 4px solid #eb8205;
border: 4px solid #EC8000;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;

View File

@ -1,33 +1,30 @@
.add-item-btn {
cursor: pointer;
display: flex;
align-items: center;
height: 40px;
font-size: 14px;
font-weight: 500;
border-top: 1px solid #dedede;
background: #fff;
padding: 0 1rem;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
position: relative;
height: 30px;
padding: 0 10px;
overflow: hidden;
}
.add-item-btn:hover {
background-color: #f5f5f5;
cursor: pointer;
background: #f5f5f5;
}
.add-item-btn .dtable-icon-add-table {
.add-item-btn .seafile-multicolor-icon-add-table {
margin-right: 10px;
font-size: 12px;
font-weight: 600;
transform: none;
fill: #666;
}
.add-item-btn .add-new-option {
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.add-item-btn .description {
flex: 1;
}

View File

@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import '../../css/common-add-tool.css';
import Icon from '../icon';
function CommonAddTool(props) {
const { callBack, footerName, className, addIconClassName } = props;
import './index.css';
function CommonAddTool({ callBack, footerName, className, addIconClassName, hideIcon, style }) {
return (
<div className={`add-item-btn ${className ? className : ''}`} onClick={(e) => {callBack(e);}}>
<span className={`sf3-font sf3-font-enlarge mr-2 ${addIconClassName || ''}`}></span>
<span className='add-new-option' title={footerName}>{footerName}</span>
<div className={`add-item-btn ${className ? className : ''}`} style={style} onClick={(e) => {callBack(e);}}>
{!hideIcon && <Icon symbol="add-table" className={addIconClassName} />}
<span className="description text-truncate">{footerName}</span>
</div>
);
}

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api';
import { siteRoot, isPro, gettext, appAvatarURL, enableSSOToThirdpartWebsite } from '../../utils/constants';
@ -35,10 +34,6 @@ class Account extends Component {
this.handleProps();
}
getContainer = () => {
return ReactDOM.findDOMNode(this);
};
handleProps = () => {
if (this.state.showInfo) {
this.addEvents();
@ -61,9 +56,7 @@ class Account extends Component {
handleDocumentClick = (e) => {
if (e && (e.which === 3 || (e.type === 'keyup' && e.which !== Utils.keyCodes.tab))) return;
const container = this.getContainer();
if (container.contains(e.target) && container !== e.target && (e.type !== 'keyup' || e.which === Utils.keyCodes.tab)) {
if (this.accountDOM && this.accountDOM.contains(e.target) && this.accountDOM !== e.target && (e.type !== 'keyup' || e.which === Utils.keyCodes.tab)) {
return;
}
@ -101,8 +94,9 @@ class Account extends Component {
renderMenu = () => {
let data;
const { isStaff, isOrgStaff, isInstAdmin } = this.state;
const { isAdminPanel = false } = this.props;
if (this.props.isAdminPanel) {
if (isAdminPanel) {
if (isStaff) {
data = {
url: siteRoot,
@ -148,7 +142,7 @@ class Account extends Component {
render() {
return (
<div id="account">
<div id="account" ref={ref => this.accountDOM = ref}>
<a id="my-info" href="#" onClick={this.onClickAccount} className="account-toggle no-deco d-none d-md-block" aria-label={gettext('View profile and more')}>
{this.renderAvatar()}
</a>
@ -180,10 +174,6 @@ class Account extends Component {
}
}
Account.defaultProps = {
isAdminPanel: false
};
Account.propTypes = propTypes;
export default Account;

View File

@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
function CommonUndoTool(props) {
const style = {
color: 'rgb(71, 184, 129)',
marginLeft: '8px',
paddingBottom: '1px',
borderBottom: '1px solid rgb(71, 184, 129)',
cursor: 'pointer',
};
return (
<span onClick={(e) => {e.stopPropagation(); props.onUndoOperation(e);}} style={style}>{gettext('Undo')}</span>
);
}
CommonUndoTool.propTypes = {
onUndoOperation: PropTypes.func.isRequired,
};
export default CommonUndoTool;

View File

@ -3,6 +3,21 @@ export const EVENT_BUS_TYPE = {
CURRENT_LIBRARY_CHANGED: 'current_library_changed',
SEARCH_LIBRARY_CONTENT: 'search_library_content',
// group
ADD_NEW_GROUP: 'add_new_group',
ADD_SHARED_REPO_INTO_GROUP: 'add_shared_repo_into_group',
UNSHARE_REPO_TO_GROUP: 'unshare_repo_to_group',
RESTORE_IMAGE: 'restore_image',
OPEN_MARKDOWN_DIALOG: 'open_markdown_dialog',
OPEN_MARKDOWN: 'open_markdown',
// migrate tags
OPEN_TREE_PANEL: 'open_tree_panel',
OPEN_LIBRARY_SETTINGS_TAGS: 'open_library_settings_tags',
// tags
TAG_STATUS: 'tag_status',
TAGS_DATA: 'tags_data',
SELECT_TAG: 'select_tag',
UPDATE_SELECTED_TAG: 'update_selected_tag',
};

View File

@ -1,5 +1,16 @@
class EventBus {
subscribers = {};
constructor() {
this.instance = null;
this.subscribers = {};
}
static getInstance() {
if (this.instance) return this.instance;
this.instance = new EventBus();
return this.instance;
}
subscribe(type, handler) {
if (!this.subscribers[type]) {

View File

@ -0,0 +1,59 @@
import React, { useState, useEffect, useRef, useMemo } from 'react';
import PropTypes from 'prop-types';
const FixedWidthTable = ({ className, headers, theadOptions = {}, children }) => {
const [containerWidth, setContainerWidth] = useState(0);
const fixedWidth = useMemo(() => headers.reduce((pre, cur) => cur.isFixed ? cur.width + pre : pre, 0), [headers]);
const containerRef = useRef(null);
useEffect(() => {
const container = containerRef.current;
const handleResize = () => {
if (!container) return;
setContainerWidth(container.offsetWidth);
};
const resizeObserver = new ResizeObserver(handleResize);
container && resizeObserver.observe(container);
return () => {
container && resizeObserver.unobserve(container);
};
}, []);
return (
<table ref={containerRef} className={className}>
<thead { ...theadOptions }>
<tr>
{headers.map((header, index) => {
const { width, isFixed, className, onClick = () => {}, title = '', ariaLabel = '' } = header;
return (
<th
key={index}
style={{ width: isFixed ? width : (containerWidth - fixedWidth) * width }}
className={className}
onClick={onClick}
title={title}
aria-label={ariaLabel}
>
{header.children}
</th>
);
})}
</tr>
</thead>
<tbody>
{children}
</tbody>
</table>
);
};
FixedWidthTable.propTypes = {
className: PropTypes.string,
headers: PropTypes.array,
theadOptions: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.number]),
};
export default FixedWidthTable;

View File

@ -0,0 +1,96 @@
.group-select {
position: relative;
}
.group-select.custom-select {
display: flex;
padding: 5px 10px;
border-radius: 3px;
align-items: center;
justify-content: space-between;
max-width: 900px;
user-select: none;
text-align: left;
border-color: 1px solid rgba(0, 40, 100, 0.12);
height: auto;
min-height: 38px;
cursor: pointer;
}
.group-select.custom-select:focus,
.group-select.custom-select.focus {
border-color: #1991eb !important;
box-shadow: 0 0 0 2px rgba(70, 127, 207, 0.25);
}
.group-select.custom-select.disabled:focus,
.group-select.custom-select.focus.disabled,
.group-select.custom-select.disabled:hover {
border-color: rgba(0, 40, 100, 0.12) !important;
box-shadow: unset;
cursor: default;
}
.group-select .sf3-font-down {
display: inline-block;
color: #999;
transform: translateY(2px);
transition: all 0.1s;
font-size: 14px !important;
}
.group-select .sf3-font-down:hover {
color: #666;
}
.group-select .selected-option {
display: flex;
flex: 1;
overflow: hidden;
flex-wrap: nowrap;
align-items: center;
justify-content: space-between;
background: #fff;
}
.group-select.selector-collaborator .option-group .option-group-content {
padding: 10px;
}
.group-select.custom-select.selector-collaborator .option-group .option-group-content {
padding: 10px 0;
}
.group-select.custom-select.selector-collaborator .option {
padding: 5px 0 5px 10px !important;
line-height: 20px;
}
.group-select .select-placeholder {
line-height: 1;
font-size: 14px;
white-space: nowrap;
}
.group-select .selected-option-show {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.group-select .selected-option-show .selected-option-item {
background-color: rgb(240, 240, 240);
border-radius: 16px;
display: flex;
align-items: center;
}
.group-select .selected-option-show .selected-option-item .selected-option-item-name {
font-size: 13px;
color: #212529;
}
.group-select .selected-option-show .selected-option-item .sf2-icon-close {
cursor: pointer;
color: rgb(103, 103, 103);
}

View File

@ -0,0 +1,138 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import ModalPortal from '../../modal-portal';
import SelectOptionGroup from './select-option-group.js';
import './index.css';
class GroupSelect extends Component {
constructor(props) {
super(props);
this.state = {
isShowSelectOptions: false
};
}
onSelectToggle = (event) => {
event.preventDefault();
if (this.state.isShowSelectOptions) event.stopPropagation();
let eventClassName = event.target.className;
if (eventClassName.indexOf('sf2-icon-close') > -1 || eventClassName === 'option-group-search') return;
if (event.target.value === '') return;
this.setState({
isShowSelectOptions: !this.state.isShowSelectOptions
});
};
onClickOutside = (event) => {
if (this.props.isShowSelected && event.target.className.includes('icon-fork-number')) {
return;
}
if (!this.selector.contains(event.target)) {
this.closeSelect();
}
};
closeSelect = () => {
this.setState({ isShowSelectOptions: false });
};
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.selectedOptions.length !== this.props.selectedOptions.length) {
// when selectedOptions change and dom rendered, calculate top
setTimeout(() => {
this.forceUpdate();
}, 1);
}
}
getSelectedOptionTop = () => {
if (!this.selector) return 38;
const { height } = this.selector.getBoundingClientRect();
return height;
};
getFilterOptions = (searchValue) => {
const { options } = this.props;
const validSearchVal = searchValue.trim().toLowerCase();
if (!validSearchVal) return options || [];
return options.filter(option => option.name.toLowerCase().includes(validSearchVal));
};
render() {
let { className, selectedOptions, options, placeholder, searchPlaceholder, noOptionsPlaceholder, isInModal } = this.props;
return (
<div
ref={(node) => this.selector = node}
className={classnames('group-select custom-select',
{ 'focus': this.state.isShowSelectOptions },
className
)}
onClick={this.onSelectToggle}>
<div className="selected-option">
{selectedOptions.length > 0 ?
<span className="selected-option-show">
{selectedOptions.map(item =>
<span key={item.id} className="selected-option-item mr-1 px-1">
<span className='selected-option-item-name'>{item.name}</span>
<i className="sf2-icon-close ml-1" onClick={() => {this.props.onDeleteOption(item);}}></i>
</span>
)}
</span>
:
<span className="select-placeholder">{placeholder}</span>
}
<i className="sf3-font-down sf3-font"></i>
</div>
{this.state.isShowSelectOptions && !isInModal && (
<SelectOptionGroup
selectedOptions={selectedOptions}
top={this.getSelectedOptionTop()}
options={options}
onSelectOption={this.props.onSelectOption}
searchPlaceholder={searchPlaceholder}
noOptionsPlaceholder={noOptionsPlaceholder}
onClickOutside={this.onClickOutside}
closeSelect={this.closeSelect}
getFilterOptions={this.getFilterOptions}
/>
)}
{this.state.isShowSelectOptions && isInModal && (
<ModalPortal>
<SelectOptionGroup
className={className}
selectedOptions={selectedOptions}
position={this.selector.getBoundingClientRect()}
isInModal={isInModal}
top={this.getSelectedOptionTop()}
options={options}
onSelectOption={this.props.onSelectOption}
searchPlaceholder={searchPlaceholder}
noOptionsPlaceholder={noOptionsPlaceholder}
onClickOutside={this.onClickOutside}
closeSelect={this.closeSelect}
getFilterOptions={this.getFilterOptions}
/>
</ModalPortal>
)}
</div>
);
}
}
GroupSelect.propTypes = {
className: PropTypes.string,
selectedOptions: PropTypes.array,
options: PropTypes.array,
placeholder: PropTypes.string,
onSelectOption: PropTypes.func,
onDeleteOption: PropTypes.func,
searchable: PropTypes.bool,
searchPlaceholder: PropTypes.string,
noOptionsPlaceholder: PropTypes.string,
isInModal: PropTypes.bool, // if select component in a modal (option group need ModalPortal to show)
};
export default GroupSelect;

View File

@ -0,0 +1,46 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Option extends Component {
onSelectOption = (e) => {
e.stopPropagation();
this.props.onSelectOption(this.props.option);
};
onMouseEnter = () => {
if (!this.props.disableHover) {
this.props.changeIndex(this.props.index);
}
};
onMouseLeave = () => {
if (!this.props.disableHover) {
this.props.changeIndex(-1);
}
};
render() {
return (
<div
className={this.props.isActive ? 'option option-active' : 'option'}
onClick={this.onSelectOption}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
>{this.props.children}
</div>
);
}
}
Option.propTypes = {
index: PropTypes.number,
isActive: PropTypes.bool,
changeIndex: PropTypes.func,
option: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
onSelectOption: PropTypes.func,
disableHover: PropTypes.bool,
};
export default Option;

View File

@ -0,0 +1,86 @@
.option-group {
position: absolute;
left: 0;
min-height: 60px;
max-height: 300px;
min-width: 100%;
max-width: 15rem;
padding: 0.5rem 0;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
background: #fff;
border: 1px solid rgba(0, 40, 100, 0.12);
border-radius: 3px;
z-index: 10001;
}
.option-group .option-group-search {
width: 100%;
padding: 6px 10px;
min-width: 170px;
}
.option-group-search .form-control {
height: 31px;
}
.option-group .none-search-result {
height: 100px;
width: 100%;
padding: 10px;
color: #666666;
}
.option-group .option-group-content {
max-height: 252px;
overflow-y: auto;
}
.option {
display: block;
width: 100%;
line-height: 24px;
padding: 6px 10px;
clear: both;
font-weight: 400;
text-align: inherit;
background-color: transparent;
border: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
align-items: center;
justify-content: space-between;
}
.option.option-active {
background-color: #20a0ff;
color: #fff;
cursor: pointer;
}
.option.option-active .sf2-icon-tick,
.option.option-active .select-option-name {
color: #fff !important;
}
.option .select-option-name .single-select-option {
margin: 0 0 0 12px;
}
.option .select-option-name .multiple-select-option {
margin: 0;
}
.option-group-selector-single-select .select-option-name {
display: flex;
align-items: center;
justify-content: space-between;
}
.option-group-selector-single-select .option:hover,
.option-group-selector-single-select .option.option-active,
.option-group-selector-multiple-select .option:hover,
.option-group-selector-multiple-select .option.option-active {
background-color: #f5f5f5;
}

View File

@ -0,0 +1,218 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import SearchInput from '../../search-input';
import Option from './option';
import KeyCodes from '../../../constants/keyCodes';
import './select-option-group.css';
const OPTION_HEIGHT = 32;
class SelectOptionGroup extends Component {
constructor(props) {
super(props);
this.state = {
searchVal: '',
activeIndex: -1,
disableHover: false,
};
this.filterOptions = null;
this.timer = null;
this.searchInputRef = React.createRef();
}
componentDidMount() {
window.addEventListener('keydown', this.onHotKey);
document.addEventListener('mousedown', this.handleDocumentClick);
setTimeout(() => {
this.resetMenuStyle();
}, 1);
}
componentWillUnmount() {
this.filterOptions = null;
this.timer && clearTimeout(this.timer);
window.removeEventListener('keydown', this.onHotKey);
document.removeEventListener('mousedown', this.handleDocumentClick);
}
handleDocumentClick = (e) => {
this.props.onClickOutside(e);
};
resetMenuStyle = () => {
const { isInModal, position } = this.props;
const { top, height } = this.optionGroupRef.getBoundingClientRect();
if (isInModal) {
if (position.y + position.height + height > window.innerHeight) {
this.optionGroupRef.style.top = (position.y - height) + 'px';
}
this.optionGroupRef.style.opacity = 1;
}
else {
if (height + top > window.innerHeight) {
const borderWidth = 2;
this.optionGroupRef.style.top = -1 * (height + borderWidth) + 'px';
}
}
};
onHotKey = (event) => {
const keyCode = event.keyCode;
if (keyCode === KeyCodes.UpArrow) {
this.onPressUp();
} else if (keyCode === KeyCodes.DownArrow) {
this.onPressDown();
} else if (keyCode === KeyCodes.Enter) {
let option = this.filterOptions && this.filterOptions[this.state.activeIndex];
if (option) {
this.props.onSelectOption(option);
}
} else if (keyCode === KeyCodes.Tab || keyCode === KeyCodes.Escape) {
this.props.closeSelect();
}
};
onPressUp = () => {
if (this.state.activeIndex > 0) {
this.setState({ activeIndex: this.state.activeIndex - 1 }, () => {
this.scrollContent();
});
}
};
onPressDown = () => {
if (this.filterOptions && this.state.activeIndex < this.filterOptions.length - 1) {
this.setState({ activeIndex: this.state.activeIndex + 1 }, () => {
this.scrollContent();
});
}
};
onMouseDown = (e) => {
const { isInModal } = this.props;
// prevent event propagation when click option or search input
if (isInModal) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
};
scrollContent = () => {
const { offsetHeight, scrollTop } = this.optionGroupContentRef;
this.setState({ disableHover: true });
this.timer = setTimeout(() => {
this.setState({ disableHover: false });
}, 500);
if (this.state.activeIndex * OPTION_HEIGHT === 0) {
this.optionGroupContentRef.scrollTop = 0;
return;
}
if (this.state.activeIndex * OPTION_HEIGHT < scrollTop) {
this.optionGroupContentRef.scrollTop = scrollTop - OPTION_HEIGHT;
}
else if (this.state.activeIndex * OPTION_HEIGHT > offsetHeight + scrollTop) {
this.optionGroupContentRef.scrollTop = scrollTop + OPTION_HEIGHT;
}
};
changeIndex = (index) => {
this.setState({ activeIndex: index });
};
onChangeSearch = (searchVal) => {
this.setState({ searchVal: searchVal || '', activeIndex: -1, });
};
clearValue = () => {
this.setState({ searchVal: '', activeIndex: -1, });
};
renderOptGroup = (searchVal) => {
let { noOptionsPlaceholder, onSelectOption, selectedOptions } = this.props;
this.filterOptions = this.props.getFilterOptions(searchVal);
if (this.filterOptions.length === 0) {
return (
<div className="none-search-result">{noOptionsPlaceholder}</div>
);
}
return this.filterOptions.map((option, index) => {
const isSelected = selectedOptions.some(item => item.id === option.id);
return (
<Option
key={`${option.id}-${index}`}
index={index}
isActive={this.state.activeIndex === index}
option={option}
onSelectOption={onSelectOption}
changeIndex={this.changeIndex}
disableHover={this.state.disableHover}
>
<div className='option-label'>{option.label}</div>
{isSelected && <i className="sf2-icon-tick text-gray font-weight-bold"></i>}
</Option>
);
});
};
render() {
const { searchPlaceholder, top, left, minWidth, isInModal, position, className } = this.props;
let { searchVal } = this.state;
let style = { top: top || 0, left: left || 0 };
if (minWidth) {
style = { top: top || 0, left: left || 0, minWidth };
}
if (isInModal) {
style = {
position: 'fixed',
left: position.x,
top: position.y + position.height,
minWidth: position.width,
opacity: 0,
};
}
return (
<div
className={classnames('pt-0 option-group', className ? 'option-group-' + className : '')}
ref={(ref) => this.optionGroupRef = ref}
style={style}
onMouseDown={this.onMouseDown}
>
<div className="option-group-search position-relative">
<SearchInput
className="option-search-control"
autoFocus={isInModal}
placeholder={searchPlaceholder}
onChange={this.onChangeSearch}
ref={this.searchInputRef}
/>
</div>
<div className="option-group-content" ref={(ref) => this.optionGroupContentRef = ref}>
{this.renderOptGroup(searchVal)}
</div>
</div>
);
}
}
SelectOptionGroup.propTypes = {
top: PropTypes.number,
left: PropTypes.number,
minWidth: PropTypes.number,
options: PropTypes.array,
onSelectOption: PropTypes.func,
searchPlaceholder: PropTypes.string,
noOptionsPlaceholder: PropTypes.string,
onClickOutside: PropTypes.func.isRequired,
closeSelect: PropTypes.func.isRequired,
getFilterOptions: PropTypes.func.isRequired,
selectedOptions: PropTypes.array,
isInModal: PropTypes.bool,
position: PropTypes.object,
className: PropTypes.string,
};
export default SelectOptionGroup;

View File

@ -4,6 +4,8 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { gettext, siteRoot } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { processor } from '@seafile/seafile-editor';
import '../../css/notice-item.css';
const propTypes = {
noticeItem: PropTypes.object.isRequired,
@ -17,12 +19,16 @@ const MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group';
const MSG_TYPE_REPO_TRANSFER = 'repo_transfer';
const MSG_TYPE_FILE_UPLOADED = 'file_uploaded';
const MSG_TYPE_FOLDER_UPLOADED = 'folder_uploaded';
const MSG_TYPE_FILE_COMMENT = 'file_comment';
// const MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted';
const MSG_TYPE_REPO_MONITOR = 'repo_monitor';
const MSG_TYPE_DELETED_FILES = 'deleted_files';
const MSG_TYPE_SAML_SSO_FAILED = 'saml_sso_failed';
const MSG_TYPE_REPO_SHARE_PERM_CHANGE = 'repo_share_perm_change';
const MSG_TYPE_REPO_SHARE_PERM_DELETE = 'repo_share_perm_delete';
const MSG_TYPE_FACE_CLUSTER = 'face_cluster';
const MSG_TYPE_SEADOC_REPLY = 'reply';
const MSG_TYPE_SEADOC_COMMENT = 'comment';
dayjs.extend(relativeTime);
@ -33,36 +39,44 @@ class NoticeItem extends React.Component {
let noticeType = noticeItem.type;
let detail = noticeItem.detail;
if (noticeType === MSG_TYPE_ADD_USER_TO_GROUP) {
let avatar_url = detail.group_staff_avatar_url;
let groupStaff = detail.group_staff_name;
// group name does not support special characters
let userHref = siteRoot + 'profile/' + detail.group_staff_email + '/';
let groupHref = siteRoot + 'group/' + detail.group_id + '/';
let groupName = detail.group_name;
let notice = gettext('User {user_link} has added you to {group_link}');
let userLink = '<a href=' + userHref + '>' + groupStaff + '</a>';
let groupLink = '<a href=' + groupHref + '>' + groupName + '</a>';
notice = notice.replace('{user_link}', userLink);
notice = notice.replace('{group_link}', groupLink);
if (noticeType === MSG_TYPE_FILE_COMMENT) {
let avatar_url = detail.author_avatar_url;
let author = detail.author_name;
let fileName = detail.file_name;
let fileUrl = siteRoot + 'lib/' + detail.repo_id + '/' + 'file' + detail.file_path;
// 1. handle translate
let notice = gettext('File {file_link} has a new comment form user {author}.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{file_link}', `{tagA}${fileName}{/tagA}`);
notice = notice.replace('{author}', author);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(fileUrl)}>`);
notice = notice.replace('{/tagA}', '</a>');
return { avatar_url, notice };
}
if (noticeType === MSG_TYPE_ADD_USER_TO_GROUP) {
let avatar_url = detail.group_staff_avatar_url;
let groupStaff = detail.group_staff_name;
// group name does not support special characters
let userHref = siteRoot + 'profile/' + encodeURIComponent(detail.group_staff_email) + '/';
let groupHref = siteRoot + 'group/' + detail.group_id + '/';
let groupName = detail.group_name;
let username = detail.group_staff_name;
let notice = gettext('User {user_link} has added you to {group_link}');
let userLink = '<a href=' + userHref + '>' + Utils.HTMLescape(groupStaff) + '</a>';
let groupLink = '<a href=' + groupHref + '>' + Utils.HTMLescape(groupName) + '</a>';
notice = notice.replace('{user_link}', userLink);
notice = notice.replace('{group_link}', groupLink);
return { avatar_url, notice, username };
}
if (noticeType === MSG_TYPE_REPO_SHARE) {
let avatar_url = detail.share_from_user_avatar_url;
let shareFrom = detail.share_from_user_name;
let repoName = detail.repo_name;
let repoUrl = siteRoot + 'library/' + detail.repo_id + '/' + repoName + '/';
let path = detail.path;
let notice = '';
// 1. handle translate
@ -71,21 +85,17 @@ class NoticeItem extends React.Component {
} else { // share folder
notice = gettext('{share_from} has shared a folder named {repo_link} to you.');
}
// 2. handle xss(cross-site scripting)
notice = notice.replace('{share_from}', shareFrom);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
notice = notice.replace('{/tagA}', '</a>');
return { avatar_url, notice };
return { avatar_url, notice, username: shareFrom };
}
if (noticeType === MSG_TYPE_REPO_SHARE_PERM_CHANGE) {
let avatar_url = detail.share_from_user_avatar_url;
let shareFrom = detail.share_from_user_name;
let permission = detail.permission;
@ -99,22 +109,18 @@ class NoticeItem extends React.Component {
} else { // share folder
notice = gettext('{share_from} has changed the permission of folder {repo_link} to {permission}.');
}
// 2. handle xss(cross-site scripting)
notice = notice.replace('{share_from}', shareFrom);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = notice.replace('{permission}', permission);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
notice = notice.replace('{/tagA}', '</a>');
return { avatar_url, notice };
return { avatar_url, notice, username: shareFrom };
}
if (noticeType === MSG_TYPE_REPO_SHARE_PERM_DELETE) {
let avatar_url = detail.share_from_user_avatar_url;
let shareFrom = detail.share_from_user_name;
let repoName = detail.repo_name;
@ -126,26 +132,20 @@ class NoticeItem extends React.Component {
} else { // share folder
notice = gettext('{share_from} has cancelled the sharing of folder {repo_name}.');
}
// 2. handle xss(cross-site scripting)
notice = notice.replace('{share_from}', shareFrom);
notice = notice.replace('{repo_name}', repoName);
notice = Utils.HTMLescape(notice);
return { avatar_url, notice };
return { avatar_url, notice, username: shareFrom };
}
if (noticeType === MSG_TYPE_REPO_SHARE_TO_GROUP) {
let avatar_url = detail.share_from_user_avatar_url;
let shareFrom = detail.share_from_user_name;
let repoName = detail.repo_name;
let repoUrl = siteRoot + 'library/' + detail.repo_id + '/' + repoName + '/';
let groupUrl = siteRoot + 'group/' + detail.group_id + '/';
let groupName = detail.group_name;
let path = detail.path;
let notice = '';
// 1. handle translate
@ -154,60 +154,50 @@ class NoticeItem extends React.Component {
} else {
notice = gettext('{share_from} has shared a folder named {repo_link} to group {group_link}.');
}
// 2. handle xss(cross-site scripting)
notice = notice.replace('{share_from}', shareFrom);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = notice.replace('{group_link}', `{tagB}${groupName}{/tagB}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
notice = notice.replace('{/tagA}', '</a>');
notice = notice.replace('{tagB}', `<a href='${Utils.encodePath(groupUrl)}'>`);
notice = notice.replace('{/tagB}', '</a>');
return { avatar_url, notice };
return { avatar_url, notice, username: shareFrom };
}
if (noticeType === MSG_TYPE_REPO_TRANSFER) {
let avatar_url = detail.transfer_from_user_avatar_url;
let repoOwner = detail.transfer_from_user_name;
let repoName = detail.repo_name;
let repoUrl = siteRoot + 'library/' + detail.repo_id + '/' + repoName + '/';
// 1. handle translate
let notice = gettext('{user} has transfered a library named {repo_link} to you.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{user}', repoOwner);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(repoUrl)}>`);
notice = notice.replace('{/tagA}', '</a>');
return { avatar_url, notice };
return { avatar_url, notice, username: repoOwner };
}
if (noticeType === MSG_TYPE_FILE_UPLOADED) {
let avatar_url = detail.uploaded_user_avatar_url;
let fileName = detail.file_name;
let fileLink = siteRoot + 'lib/' + detail.repo_id + '/' + 'file' + detail.file_path;
let folderName = detail.folder_name;
let folderLink = siteRoot + 'library/' + detail.repo_id + '/' + detail.repo_name + detail.folder_path;
let notice = '';
if (detail.repo_id) { // todo is repo exist ?
// 1. handle translate
notice = gettext('A file named {upload_file_link} is uploaded to {uploaded_link}.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{upload_file_link}', `{tagA}${fileName}{/tagA}`);
notice = notice.replace('{uploaded_link}', `{tagB}${folderName}{/tagB}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(fileLink)}>`);
notice = notice.replace('{/tagA}', '</a>');
@ -216,7 +206,6 @@ class NoticeItem extends React.Component {
} else {
// 1. handle translate
notice = gettext('A file named {upload_file_link} is uploaded.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{upload_file_link}', `${fileName}`);
notice = Utils.HTMLescape(notice);
@ -340,15 +329,22 @@ class NoticeItem extends React.Component {
}
if (noticeType === MSG_TYPE_DELETED_FILES) {
const {
repo_id,
repo_name,
} = detail;
const { repo_id, repo_name } = detail;
const repoURL = `${siteRoot}library/${repo_id}/${encodeURIComponent(repo_name)}/`;
const repoLink = `<a href=${repoURL} target="_blank">${Utils.HTMLescape(repo_name)}</a>`;
let notice = gettext('Your library {libraryName} has recently deleted a large number of files.');
notice = notice.replace('{libraryName}', repoLink);
return { avatar_url: null, notice };
}
if (noticeType === MSG_TYPE_FACE_CLUSTER) {
let repo_id = detail.repo_id;
let repo_name = detail.repo_name;
const repoURL = `${siteRoot}library/${repo_id}/${encodeURIComponent(repo_name)}/`;
const repoLink = `<a href=${repoURL} target="_blank">${Utils.HTMLescape(repo_name)}</a>`;
let notice = gettext('Your library {libraryName} has recently deleted a large number of files.');
let notice = gettext('Face recognition is done for library {libraryName}.');
notice = notice.replace('{libraryName}', repoLink);
return { avatar_url: null, notice };
@ -357,15 +353,70 @@ class NoticeItem extends React.Component {
if (noticeType === MSG_TYPE_SAML_SSO_FAILED) {
const { error_msg } = detail;
let notice = gettext(error_msg);
return { avatar_url: null, notice };
}
if (noticeType === MSG_TYPE_SEADOC_COMMENT) {
let avatar_url = detail.avatar_url;
let notice = detail.comment;
let username = detail.user_name;
let is_resolved = detail.is_resolved;
let sdoc_name = detail.sdoc_name;
const repo_id = detail.repo_id;
const sdoc_path = detail.sdoc_path;
const sdoc_href = siteRoot + 'lib/' + repo_id + '/file' + sdoc_path;
let sdoc_link = '<a href=' + sdoc_href + '>' + sdoc_name + '</a>';
processor.process(notice, (error, vfile) => {
notice = String(vfile);
});
if (is_resolved) {
if (detail.resolve_comment && detail.resolve_comment !== '\u200B') {
notice = gettext('Marked "{resolve_comment}" as resolved in document {sdoc_link}');
notice = notice.replace('{resolve_comment}', detail.resolve_comment);
notice = notice.replace('{sdoc_link}', sdoc_link);
} else {
notice = gettext('Marked as resolved in document {sdoc_link}');
notice = notice.replace('{sdoc_link}', sdoc_link);
}
} else {
notice = gettext('Added a new comment in document {sdoc_link}:').replace('{sdoc_link}', sdoc_link) + notice;
}
return { avatar_url, username, notice };
}
if (noticeType === MSG_TYPE_SEADOC_REPLY) {
let avatar_url = detail.avatar_url;
let notice = detail.reply;
let username = detail.user_name;
let is_resolved = detail.is_resolved;
let sdoc_name = detail.sdoc_name;
const repo_id = detail.repo_id;
const sdoc_path = detail.sdoc_path;
const sdoc_href = siteRoot + 'lib/' + repo_id + '/file' + sdoc_path;
let sdoc_link = '<a href=' + sdoc_href + '>' + sdoc_name + '</a>';
processor.process(notice, (error, vfile) => {
notice = String(vfile);
});
if (is_resolved) {
if (detail.resolve_comment && detail.resolve_comment !== '\u200B') {
notice = gettext('Marked "{resolve_comment}" as resolved in document {sdoc_link}');
notice = notice.replace('{resolve_comment}', detail.resolve_comment);
notice = notice.replace('{sdoc_link}', sdoc_link);
} else {
notice = gettext('Marked as resolved in document {sdoc_link}');
notice = notice.replace('{sdoc_link}', sdoc_link);
}
} else {
notice = gettext('Added a new reply in document {sdoc_link}:').replace('{sdoc_link}', sdoc_link) + notice;
}
return { avatar_url, username, notice };
}
// if (noticeType === MSG_TYPE_GUEST_INVITATION_ACCEPTED) {
// }
return { avatar_url: null, notice: null };
return { avatar_url: null, notice: null, username: null };
}
onNoticeItemClick = () => {
@ -378,16 +429,19 @@ class NoticeItem extends React.Component {
render() {
let noticeItem = this.props.noticeItem;
let { avatar_url, notice } = this.generatorNoticeInfo();
let { avatar_url, username, notice } = this.generatorNoticeInfo();
if (!avatar_url && !notice) {
return '';
}
return this.props.tr ? (
<tr className={noticeItem.seen ? 'read' : 'unread font-weight-bold'}>
<tr className='notification-item'>
<td className="text-center">
{!noticeItem.seen && <span className="notification-point" onClick={this.onMarkNotificationRead}></span>}
</td>
<td>
<img src={avatar_url} width="32" height="32" className="avatar" alt="" />
<span className="ml-2 notification-user-name">{username || gettext('System')}</span>
</td>
<td className="pr-1 pr-md-8">
<p className="m-0" dangerouslySetInnerHTML={{ __html: notice }}></p>
@ -397,13 +451,21 @@ class NoticeItem extends React.Component {
</td>
</tr>
) : (
<li onClick={this.onNoticeItemClick} className={noticeItem.seen ? 'read' : 'unread'}>
<div className="notice-item">
<div className="main-info">
<img src={avatar_url} width="32" height="32" className="avatar" alt=""/>
<p className="brief" dangerouslySetInnerHTML={{ __html: notice }}></p>
<li className='notification-item' onClick={this.onNoticeItemClick}>
<div className="notification-item-header">
{!noticeItem.seen &&
<span className="notification-point" onClick={this.onMarkNotificationRead}></span>
}
<div className="notification-header-info">
<div className="notification-user-detail">
<img className="notification-user-avatar" src={avatar_url} alt="" />
<span className="ml-2 notification-user-name">{username || gettext('System')}</span>
</div>
<span className="notification-time">{dayjs(noticeItem.time).fromNow()}</span>
</div>
<p className="time">{dayjs(noticeItem.time).fromNow()}</p>
</div>
<div className="notification-content-wrapper">
<div dangerouslySetInnerHTML={{ __html: notice }}></div>
</div>
</li>
);

View File

@ -5,7 +5,7 @@
.notification-container {
position: absolute;
background: #fff;
width: 320px;
width: 400px;
right: -16px;
top: -1px;
border-radius: 3px;
@ -27,22 +27,12 @@
font-size: 16px;
font-weight: 600;
position: relative;
overflow: hidden;
}
.notification-container .notification-header .notification-close-icon {
.notification-container .notification-header .seahub-modal-btn {
position: absolute;
right: 14px;
height: 24px;
width: 24px;
text-align: center;
cursor: pointer;
color: #000;
opacity: 0.5;
font-weight: 700;
}
.notification-container .notification-header .notification-close-icon:hover {
opacity: 0.75;
}
.notification-container .notification-body {
@ -65,21 +55,15 @@
margin-left: 20px;
}
.notification-container .notification-body .mark-notifications {
color: #b4b4b4;
.notification-container .mark-all-read {
color: #666;
cursor: pointer;
border-bottom: 1px solid #ededed;
height: 36px;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 1rem;
}
.notification-container .notification-body .mark-notifications:hover {
text-decoration: underline;
}
.notification-body .notification-list-container {
max-height: 260px;
overflow: auto;
@ -190,3 +174,33 @@
.notification-body .notification-footer:hover {
text-decoration: underline;
}
.notification-container .notification-body .mark-notifications {
display: flex;
justify-content: space-between;
border-bottom: 1px solid #ededed;
padding-left: 15px;
}
.notification-container .notification-body .mark-notifications .mark-all-read:hover {
text-decoration: underline;
}
.notification-container .notification-body .nav .nav-item .nav-link {
height: 46px;
margin-right: 15px;
margin-left: 15px;
font-size: 14px;
color: #212529;
}
.notification-container .notification-body .nav .nav-item .nav-link.active {
color: #ED7109;
}
@media (max-width: 768px) {
.notification-container {
right: -60px;
width: 360px;
}
}

View File

@ -1,26 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Popover } from 'reactstrap';
import { gettext } from '../../../utils/constants';
import SeahubModalCloseIcon from '../seahub-modal-close';
import './index.css';
export default class NotificationPopover extends React.Component {
static propTypes = {
headerText: PropTypes.string.isRequired,
bodyText: PropTypes.string.isRequired,
footerText: PropTypes.string.isRequired,
onNotificationListToggle: PropTypes.func,
onNotificationDialogToggle: PropTypes.func,
listNotifications: PropTypes.func,
onMarkAllNotifications: PropTypes.func,
children: PropTypes.any,
};
static defaultProps = {
headerText: '',
bodyText: '',
footerText: '',
};
class NotificationPopover extends React.Component {
componentDidMount() {
document.addEventListener('mousedown', this.handleOutsideClick, true);
@ -47,8 +33,12 @@ export default class NotificationPopover extends React.Component {
}
};
tabItemClick = (tab) => {
this.props.tabItemClick(tab);
};
render() {
const { headerText, bodyText, footerText } = this.props;
const { headerText = '', bodyText = '', footerText = '', currentTab, generalNoticeListUnseen, discussionNoticeListUnseen } = this.props;
return (
<Popover
className="notification-wrapper"
@ -59,17 +49,44 @@ export default class NotificationPopover extends React.Component {
placement="bottom"
>
<div className="notification-container" ref={ref => this.notificationContainerRef = ref}>
<div className="notification-header">
<div className="notification-header modal">
{headerText}
<span className="sf3-font sf3-font-x-01 notification-close-icon" onClick={this.props.onNotificationListToggle}></span>
<SeahubModalCloseIcon toggle={this.props.onNotificationListToggle} />
</div>
<div className="notification-body">
<div className="mark-notifications" onClick={this.props.onMarkAllNotifications}>{bodyText}</div>
<div className="mark-notifications">
<ul className="nav">
<li className="nav-item" onClick={() => this.tabItemClick('general')}>
<span className={`nav-link ${currentTab === 'general' ? 'active' : ''}`}>
{gettext('General')}
{generalNoticeListUnseen > 0 && <span>({generalNoticeListUnseen})</span>}
</span>
</li>
<li className="nav-item" onClick={() => this.tabItemClick('discussion')}>
<span className={`nav-link ${currentTab === 'discussion' ? 'active' : ''}`}>
{gettext('Discussion')}
{discussionNoticeListUnseen > 0 && <span>({discussionNoticeListUnseen})</span>}
</span>
</li>
</ul>
<span className="mark-all-read" onClick={this.props.onMarkAllNotifications}>
{bodyText}
</span>
</div>
{currentTab === 'general' &&
<div className="notification-list-container" onScroll={this.onHandleScroll} ref={ref => this.notificationListRef = ref}>
<div ref={ref => this.notificationsWrapperRef = ref}>
{this.props.children}
</div>
</div>
}
{currentTab === 'discussion' &&
<div className="notification-list-container" onScroll={this.onHandleScroll} ref={ref => this.notificationListRef = ref}>
<div ref={ref => this.notificationsWrapperRef = ref}>
{this.props.children}
</div>
</div>
}
<div className="notification-footer" onClick={this.onNotificationDialogToggle}>{footerText}</div>
</div>
</div>
@ -77,3 +94,20 @@ export default class NotificationPopover extends React.Component {
);
}
}
NotificationPopover.propTypes = {
headerText: PropTypes.string.isRequired,
bodyText: PropTypes.string.isRequired,
footerText: PropTypes.string.isRequired,
onNotificationListToggle: PropTypes.func,
onNotificationDialogToggle: PropTypes.func,
listNotifications: PropTypes.func,
onMarkAllNotifications: PropTypes.func,
tabItemClick: PropTypes.func,
children: PropTypes.any,
currentTab: PropTypes.string,
generalNoticeListUnseen: PropTypes.number,
discussionNoticeListUnseen: PropTypes.number,
};
export default NotificationPopover;

View File

@ -12,15 +12,21 @@ class Notification extends React.Component {
super(props);
this.state = {
showNotice: false,
unseenCount: 0,
noticeList: [],
totalUnseenCount: 0,
generalNoticeList: [],
discussionNoticeList: [],
currentTab: 'general',
isShowNotificationDialog: this.getInitDialogState(),
};
}
componentDidMount() {
seafileAPI.getUnseenNotificationCount().then(res => {
this.setState({ unseenCount: res.data.unseen_count });
seafileAPI.listAllNotifications().then(res => {
this.setState({
totalUnseenCount: res.data.total_unseen_count,
generalNoticeListUnseen: res.data.general.unseen_count,
discussionNoticeListUnseen: res.data.discussion.unseen_count
});
});
}
@ -30,7 +36,7 @@ class Notification extends React.Component {
seafileAPI.updateNotifications();
this.setState({
showNotice: false,
unseenCount: 0
totalUnseenCount: 0
});
} else {
this.loadNotices();
@ -38,28 +44,65 @@ class Notification extends React.Component {
}
};
tabItemClick = (tab) => {
const { currentTab } = this.state;
if (currentTab === tab) return;
this.setState({
showNotice: true,
currentTab: tab
});
};
loadNotices = () => {
let page = 1;
let perPage = 5;
seafileAPI.listNotifications(page, perPage).then(res => {
let noticeList = res.data.notification_list;
this.setState({ noticeList: noticeList });
let perPage = 25;
seafileAPI.listAllNotifications(page, perPage).then(res => {
let generalNoticeList = res.data.general.notification_list;
let discussionNoticeList = res.data.discussion.notification_list;
let generalNoticeListUnseen = res.data.general.unseen_count;
let discussionNoticeListUnseen = res.data.discussion.unseen_count;
this.setState({
generalNoticeList: generalNoticeList,
discussionNoticeList: discussionNoticeList,
generalNoticeListUnseen: generalNoticeListUnseen,
discussionNoticeListUnseen: discussionNoticeListUnseen
});
});
};
onNoticeItemClick = (noticeItem) => {
let noticeList = this.state.noticeList.map(item => {
if (item.id === noticeItem.id) {
item.seen = true;
}
return item;
});
seafileAPI.markNoticeAsRead(noticeItem.id);
let unseenCount = this.state.unseenCount === 0 ? 0 : this.state.unseenCount - 1;
this.setState({
noticeList: noticeList,
unseenCount: unseenCount,
});
if (this.state.currentTab === 'general') {
let noticeList = this.state.generalNoticeList.map(item => {
if (item.id === noticeItem.id) {
item.seen = true;
}
return item;
});
let totalUnseenCount = this.state.totalUnseenCount === 0 ? 0 : this.state.totalUnseenCount - 1;
let generalNoticeListUnseen = this.state.generalNoticeListUnseen === 0 ? 0 : this.state.generalNoticeListUnseen - 1;
this.setState({
generalNoticeList: noticeList,
totalUnseenCount: totalUnseenCount,
generalNoticeListUnseen: generalNoticeListUnseen
});
seafileAPI.markNoticeAsRead(noticeItem.id);
}
if (this.state.currentTab === 'discussion') {
let noticeList = this.state.discussionNoticeList.map(item => {
if (item.id === noticeItem.id) {
item.seen = true;
}
return item;
});
let totalUnseenCount = this.state.totalUnseenCount === 0 ? 0 : this.state.totalUnseenCount - 1;
let discussionNoticeListUnseen = this.state.discussionNoticeListUnseen === 0 ? 0 : this.state.discussionNoticeListUnseen - 1;
this.setState({
discussionNoticeList: noticeList,
totalUnseenCount: totalUnseenCount,
discussionNoticeListUnseen: discussionNoticeListUnseen
});
seafileAPI.markSdocNoticeAsRead(noticeItem.id);
}
};
@ -79,43 +122,103 @@ class Notification extends React.Component {
};
onMarkAllNotifications = () => {
seafileAPI.updateNotifications().then(() => {
this.setState({
unseenCount: 0,
let generalNoticeListUnseen = this.state.generalNoticeListUnseen;
let discussionNoticeListUnseen = this.state.discussionNoticeListUnseen;
if (this.state.currentTab === 'general') {
seafileAPI.updateNotifications().then((res) => {
this.setState({
generalNoticeList: this.state.generalNoticeList.map(item => {
item.seen = true;
return item;
}),
generalNoticeListUnseen: 0,
totalUnseenCount: discussionNoticeListUnseen
});
}).catch((error) => {
this.setState({
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
}).catch((error) => {
this.setState({
errorMsg: Utils.getErrorMsg(error, true)
} else if (this.state.currentTab === 'discussion') {
seafileAPI.updateSdocNotifications().then((res) => {
this.setState({
discussionNoticeList: this.state.discussionNoticeList.map(item => {
item.seen = true;
return item;
}),
discussionNoticeListUnseen: 0,
totalUnseenCount: generalNoticeListUnseen
});
}).catch((error) => {
this.setState({
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
});
}
};
updateTotalUnseenCount = (noticeType) => {
if (noticeType === 'general') {
this.setState({
generalNoticeListUnseen: 0,
totalUnseenCount: this.state.discussionNoticeListUnseen
});
} else if (noticeType === 'discussion') {
this.setState({
discussionNoticeListUnseen: 0,
totalUnseenCount: this.state.generalNoticeListUnseen
});
}
};
render() {
const { unseenCount } = this.state;
const { totalUnseenCount, currentTab, generalNoticeList, discussionNoticeList, generalNoticeListUnseen, discussionNoticeListUnseen } = this.state;
return (
<div id="notifications">
<a href="#" onClick={this.onClick} className="no-deco" id="notice-icon" title={gettext('Notifications')} aria-label={gettext('Notifications')}>
<span className="sf2-icon-bell" id="notification-popover"></span>
<span className={`num ${unseenCount ? '' : 'hide'}`}>{unseenCount}</span>
<span className={`num ${totalUnseenCount ? '' : 'hide'}`}>{totalUnseenCount}</span>
</a>
{this.state.showNotice &&
<NotificationPopover
headerText={gettext('Notification')}
bodyText={gettext('Mark all as read')}
footerText={gettext('View all notifications')}
currentTab={currentTab}
onNotificationListToggle={this.onNotificationListToggle}
onNotificationDialogToggle={this.onNotificationDialogToggle}
onMarkAllNotifications={this.onMarkAllNotifications}
tabItemClick={this.tabItemClick}
generalNoticeListUnseen={generalNoticeListUnseen}
discussionNoticeListUnseen={discussionNoticeListUnseen}
>
<ul className="notice-list list-unstyled" id="notice-popover">
{this.state.noticeList.map(item => {
return (<NoticeItem key={item.id} noticeItem={item} onNoticeItemClick={this.onNoticeItemClick}/>);
})}
</ul>
{currentTab === 'general' &&
<ul className="notice-list list-unstyled" id="notice-popover">
{generalNoticeList.map(item => {
return (
<NoticeItem key={item.id} noticeItem={item} onNoticeItemClick={this.onNoticeItemClick}/>
);
})}
</ul>
}
{currentTab === 'discussion' &&
<ul className="notice-list list-unstyled" id="notice-popover">
{discussionNoticeList.map(item => {
return (
<NoticeItem key={item.id} noticeItem={item} onNoticeItemClick={this.onNoticeItemClick}/>
);
})}
</ul>
}
</NotificationPopover>
}
{this.state.isShowNotificationDialog &&
<UserNotificationsDialog onNotificationDialogToggle={this.onNotificationDialogToggle} />
<UserNotificationsDialog
onNotificationDialogToggle={this.onNotificationDialogToggle}
generalNoticeListUnseen={generalNoticeListUnseen}
discussionNoticeListUnseen={discussionNoticeListUnseen}
updateTotalUnseenCount={this.updateTotalUnseenCount}
/>
}
</div>
);

View File

@ -0,0 +1,15 @@
import React from 'react';
import { gettext } from '../../utils/constants';
import '../../css/seahub-modal-header.css';
const SeahubModalCloseIcon = (props) => {
return (
<button type="button" className={`close seahub-modal-btn ${props.className ? props.className : ''}`} data-dismiss="modal" aria-label={gettext('Close')} onClick={props.toggle}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
);
};
export default SeahubModalCloseIcon;

View File

@ -0,0 +1,21 @@
import React from 'react';
import { ModalHeader } from 'reactstrap';
import { gettext } from '../../utils/constants';
import '../../css/seahub-modal-header.css';
const SeahubModalHeader = ({ children, ...props }) => {
const customCloseBtn = (
<button type="button" className="close seahub-modal-btn" data-dismiss="modal" aria-label={gettext('Close')} onClick={props.toggle}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
);
return (
<ModalHeader {...props} close={customCloseBtn}>
{children}
</ModalHeader>
);
};
export default SeahubModalHeader;

View File

@ -1,5 +1,4 @@
import SeahubSelect from './seahub-select';
import { NoGroupMessage } from './no-group-message';
import { MenuSelectStyle, UserSelectStyle, NoOptionsStyle } from './seahub-select-style';
export { SeahubSelect, NoGroupMessage, MenuSelectStyle, UserSelectStyle, NoOptionsStyle };
export { SeahubSelect, MenuSelectStyle, UserSelectStyle, NoOptionsStyle };

View File

@ -1,16 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../../utils/constants';
import { NoOptionsStyle } from './seahub-select-style';
const NoGroupMessage = (props) => {
return (
<div {...props.innerProps} style={NoOptionsStyle}>{gettext('Group not found')}</div>
);
};
NoGroupMessage.propTypes = {
innerProps: PropTypes.any.isRequired,
};
export { NoGroupMessage };

View File

@ -1,3 +1,44 @@
// Seahub select is based on seafile-ui.css, so use the following content to override the default react-select style
const DEFAULT_CONTROL_STYLE = {
border: '1px solid rgba(0, 40, 100, 0.12) !important',
};
const FOCUS_CONTROL_STYLE = {
fontSize: '14px',
backgroundColor: '#ffffff',
borderColor: '#1991eb',
outline: '0',
boxShadow: '0 0 0 2px rgba(70, 127, 207, 0.25)',
};
const noneCallback = () => ({
display: 'none',
});
const controlCallback = (provided, state) => {
const { isDisabled, isFocused } = state;
if (isFocused && !isDisabled) {
return {
...provided,
...FOCUS_CONTROL_STYLE,
'&:hover': {
...provided,
...FOCUS_CONTROL_STYLE,
}
};
}
return {
...provided,
fontSize: '14px',
lineHeight: '1.5',
cursor: 'pointer',
...DEFAULT_CONTROL_STYLE,
'&:hover': {
...DEFAULT_CONTROL_STYLE,
}
};
};
const MenuSelectStyle = {
option: (provided, state) => {
const { isDisabled, isSelected, isFocused } = state;
@ -10,14 +51,9 @@ const MenuSelectStyle = {
},
});
},
control: (provided) => ({
...provided,
fontSize: '14px',
cursor: 'pointer',
lineHeight: '1.5',
}),
control: controlCallback,
menuPortal: base => ({ ...base, zIndex: 9999 }),
indicatorSeparator: () => {},
indicatorSeparator: noneCallback,
};
const UserSelectStyle = {
@ -29,21 +65,10 @@ const UserSelectStyle = {
backgroundColor: isFocused ? '#f5f5f5' : '#fff',
});
},
control: (provided) => ({
...provided,
fontSize: '14px',
cursor: 'pointer',
lineHeight: '1.5',
}),
indicatorSeparator: () => ({
display: 'none',
}),
dropdownIndicator: () => ({
display: 'none',
}),
clearIndicator: () => ({
display: 'none',
}),
control: controlCallback,
indicatorSeparator: noneCallback,
dropdownIndicator: noneCallback,
clearIndicator: noneCallback,
// multi select style
multiValue: (provided) => {
return {

View File

@ -1,15 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import Select, { components, createFilter } from 'react-select';
import Select, { components } from 'react-select';
import { MenuSelectStyle } from './seahub-select-style';
const DropdownIndicator = props => {
return (
components.DropdownIndicator && (
<components.DropdownIndicator {...props}>
<span className="sf3-font sf3-font-down" style={{ fontSize: '12px', marginLeft: '-2px' }} aria-hidden="true"></span>
</components.DropdownIndicator>
)
);
};
const ClearIndicator = ({ innerProps, ...props }) => {
const onMouseDown = e => {
e.nativeEvent.stopImmediatePropagation();
innerProps.onMouseDown(e);
};
props.innerProps = { ...innerProps, onMouseDown };
return <components.ClearIndicator {...props} />;
return (
<components.ClearIndicator {...props} >
<span className="sf3-font sf3-font-x-01" style={{ fontSize: '12px', marginLeft: '-2px' }} aria-hidden="true"></span>
</components.ClearIndicator>
);
};
ClearIndicator.propTypes = {
@ -40,47 +54,17 @@ Option.propTypes = {
}),
};
export default class SeahubSelect extends React.Component {
static propTypes = {
isMulti: PropTypes.bool,
options: PropTypes.array.isRequired,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]),
isSearchable: PropTypes.bool,
isClearable: PropTypes.bool,
placeholder: PropTypes.string,
classNamePrefix: PropTypes.string,
className: PropTypes.string,
form: PropTypes.string,
onChange: PropTypes.func.isRequired,
menuPortalTarget: PropTypes.string,
menuPosition: PropTypes.string,
noOptionsMessage: PropTypes.func,
innerRef: PropTypes.object,
isDisabled: PropTypes.bool,
};
static defaultProps = {
options: [],
value: {},
isDisabled: false,
isSearchable: false,
isClearable: false,
placeholder: '',
isMulti: false,
menuPortalTarget: '.modal',
noOptionsMessage: () => {
return null;
},
};
class SeahubSelect extends React.Component {
getMenuPortalTarget = () => {
return document.querySelector(this.props.menuPortalTarget);
const { menuPortalTarget = '.modal' } = this.props;
return document.querySelector(menuPortalTarget);
};
render() {
const { options, onChange, value, isSearchable, placeholder, isMulti, menuPosition, isClearable, noOptionsMessage,
classNamePrefix, innerRef, isDisabled, form, className } = this.props;
const { options = [], onChange, value = {}, isSearchable = true, placeholder = '',
isMulti = false, menuPosition, isClearable = true, noOptionsMessage = (() => { return null; }),
classNamePrefix, innerRef, isDisabled = false, form, className } = this.props;
return (
<Select
@ -93,11 +77,7 @@ export default class SeahubSelect extends React.Component {
className={className}
classNamePrefix={classNamePrefix}
styles={MenuSelectStyle}
components={{ Option, MenuList, ClearIndicator }}
filterOption={createFilter({
matchFrom: 'any',
stringify: option => `${option.data.labelValue}`,
})}
components={{ Option, DropdownIndicator, MenuList, ClearIndicator }}
placeholder={placeholder}
isSearchable={isSearchable}
isClearable={isClearable}
@ -111,3 +91,23 @@ export default class SeahubSelect extends React.Component {
);
}
}
SeahubSelect.propTypes = {
isMulti: PropTypes.bool,
options: PropTypes.array.isRequired,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]),
isSearchable: PropTypes.bool,
isClearable: PropTypes.bool,
placeholder: PropTypes.string,
classNamePrefix: PropTypes.string,
className: PropTypes.string,
form: PropTypes.string,
onChange: PropTypes.func.isRequired,
menuPortalTarget: PropTypes.string,
menuPosition: PropTypes.string,
noOptionsMessage: PropTypes.func,
innerRef: PropTypes.object,
isDisabled: PropTypes.bool,
};
export default SeahubSelect;

View File

@ -228,7 +228,7 @@ class ContextMenu extends React.Component {
onMouseMove={(e) => {e.stopPropagation();}}
>
<DropdownToggle
tag='div'
tag='span'
className="dropdown-item font-weight-normal rounded-0 d-flex align-items-center"
onMouseEnter={this.toggleSubMenuShown.bind(this, menuItem)}
>

View File

@ -1,11 +1,15 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { UncontrolledTooltip } from 'reactstrap';
import { Link } from '@gatsbyjs/reach-router';
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
import DirOperationToolbar from '../../components/toolbar/dir-operation-toolbar';
import MetadataViewName from '../../metadata/components/metadata-view-name';
import TagViewName from '../../tag/components/tag-view-name';
import { siteRoot, gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { debounce, Utils } from '../../utils/utils';
import { PRIVATE_FILE_TYPE } from '../../constants';
import { EVENT_BUS_TYPE } from '../../metadata/constants';
import { ALL_TAGS_ID } from '../../tag/constants';
const propTypes = {
currentRepoInfo: PropTypes.object.isRequired,
@ -29,7 +33,6 @@ const propTypes = {
direntList: PropTypes.array.isRequired,
repoTags: PropTypes.array.isRequired,
filePermission: PropTypes.string,
onFileTagChanged: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired,
loadDirentList: PropTypes.func.isRequired,
};
@ -114,40 +117,78 @@ class DirPath extends React.Component {
});
};
handleRefresh = debounce(() => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.RELOAD_DATA);
}, 200);
turnViewPathToLink = (pathList) => {
if (!Array.isArray(pathList) || pathList.length === 0) return null;
const [, , viewId, children] = pathList;
return (
<>
<span className="path-split">/</span>
<span className="path-item path-item-read-only">{gettext('Views')}</span>
<span className="path-split">/</span>
<span
className="path-item path-item-read-only"
role={children ? 'button' : null}
onClick={children ? this.handleRefresh : () => {}}
>
<MetadataViewName id={viewId} />
</span>
{children && (
<>
<span className="path-split">/</span>
<span className="path-item path-item-read-only">{children}</span>
</>
)}
<div className="path-item-refresh" id="sf-metadata-view-refresh" onClick={this.handleRefresh}>
<i className="sf3-font sf3-font-refresh"></i>
<UncontrolledTooltip target="sf-metadata-view-refresh" placement="bottom">
{gettext('Refresh the view')}
</UncontrolledTooltip>
</div>
</>
);
};
turnTagPathToLink = (pathList) => {
if (!Array.isArray(pathList) || pathList.length === 0) return null;
const [, , tagId, children] = pathList;
const canSelectAllTags = tagId === ALL_TAGS_ID && !!children;
return (
<>
<span className="path-split">/</span>
<span className="path-item path-item-read-only">{gettext('Tags')}</span>
<span className="path-split">/</span>
<TagViewName id={tagId} canSelectAllTags={canSelectAllTags} />
{children && (
<>
<span className="path-split">/</span>
<span className="path-item path-item-read-only">{children}</span>
</>
)}
</>
);
};
turnPathToLink = (path) => {
path = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
let pathList = path.split('/');
let nodePath = '';
if (pathList.length === 2 && !pathList[0] && pathList[1] === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) {
return null;
const pathList = path.split('/');
if (pathList.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
return this.turnViewPathToLink(pathList);
}
if (pathList.includes(PRIVATE_FILE_TYPE.TAGS_PROPERTIES)) {
return this.turnTagPathToLink(pathList);
}
let nodePath = '';
let pathElem = pathList.map((item, index) => {
if (item === '') {
return null;
}
if (index === pathList.length - 2 && item === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) {
return (
<Fragment key={index}>
<span className="path-split">/</span>
<span className="path-item">{gettext('Views')}</span>
</Fragment>
);
}
if (index === pathList.length - 1 && pathList[pathList.length - 2] === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) {
return (
<Fragment key={index}>
<span className="path-split">/</span>
<span className="path-item"><MetadataViewName id={item} /></span>
</Fragment>
);
}
if (item === '') return null;
if (index === (pathList.length - 1)) {
return (
<Fragment key={index}>
<span className="path-split">/</span>
<DirOperationToolBar
<DirOperationToolbar
path={this.props.currentPath}
repoID={this.props.repoID}
repoName={this.props.repoName}
@ -164,7 +205,7 @@ class DirPath extends React.Component {
loadDirentList={this.props.loadDirentList}
>
<span className="path-file-name">{item}</span>
</DirOperationToolBar>
</DirOperationToolbar>
</Fragment>
);
} else {
@ -189,16 +230,9 @@ class DirPath extends React.Component {
return pathElem;
};
isViewMetadata = () => {
const { currentPath } = this.props;
const path = currentPath[currentPath.length - 1] === '/' ? currentPath.slice(0, currentPath.length - 1) : currentPath;
const pathList = path.split('/');
return pathList[pathList.length - 2] === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES;
};
render() {
let { currentPath, repoName } = this.props;
let pathElem = this.turnPathToLink(currentPath);
const { currentPath, repoName } = this.props;
const pathElem = this.turnPathToLink(currentPath);
return (
<div className="path-container dir-view-path">
<span className="cur-view-path-btn mr-1" onClick={this.props.toggleTreePanel}>
@ -213,19 +247,19 @@ class DirPath extends React.Component {
);
})}
{this.props.pathPrefix && this.props.pathPrefix.length === 0 && (
<Fragment>
<>
<Link to={siteRoot + 'libraries/'} className="path-item normal" onClick={(e) => this.onTabNavClick(e, 'libraries')}>{gettext('Files')}</Link>
<span className="path-split">/</span>
</Fragment>
</>
)}
{!this.props.pathPrefix && (
<Fragment>
<>
<Link to={siteRoot + 'libraries/'} className="path-item normal" onClick={(e) => this.onTabNavClick(e, 'libraries')}>{gettext('Files')}</Link>
<span className="path-split">/</span>
</Fragment>
</>
)}
{(currentPath === '/' || currentPath === '') ?
<DirOperationToolBar
<DirOperationToolbar
path={this.props.currentPath}
repoID={this.props.repoID}
repoName={this.props.repoName}
@ -242,7 +276,7 @@ class DirPath extends React.Component {
loadDirentList={this.props.loadDirentList}
>
<span className="path-repo-name">{repoName}</span>
</DirOperationToolBar> :
</DirOperationToolbar> :
<span className="path-item" data-path="/" onClick={this.onPathClick} role="button">{repoName}</span>
}
{pathElem}

View File

@ -1,23 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
import { gettext, enableFileTags } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import TextTranslation from '../../utils/text-translation';
import SeahubPopover from '../common/seahub-popover';
import ListTagPopover from '../popover/list-tag-popover';
import ViewModes from '../../components/view-modes';
import ReposSortMenu from '../../components/repos-sort-menu';
import SortMenu from '../../components/sort-menu';
import MetadataViewToolBar from '../../metadata/components/view-toolbar';
import TagsTableSearcher from '../../tag/views/all-tags/tags-table/tags-searcher';
import { PRIVATE_FILE_TYPE } from '../../constants';
import { DIRENT_DETAIL_MODE } from '../dir-view-mode/constants';
import { ALL_TAGS_ID } from '../../tag/constants';
import AllTagsSortSetter from '../../tag/views/all-tags/tags-table/sort-setter';
import TagFilesSortSetter from '../../tag/views/tag-files/sort-setter';
const propTypes = {
repoID: PropTypes.string.isRequired,
userPerm: PropTypes.string,
currentPath: PropTypes.string.isRequired,
updateUsedRepoTags: PropTypes.func.isRequired,
onDeleteRepoTag: PropTypes.func.isRequired,
currentMode: PropTypes.string.isRequired,
switchViewMode: PropTypes.func.isRequired,
isCustomPermission: PropTypes.bool,
@ -25,163 +20,56 @@ const propTypes = {
sortOrder: PropTypes.string,
sortItems: PropTypes.func,
viewId: PropTypes.string,
onToggleDetail: PropTypes.func,
onCloseDetail: PropTypes.func,
};
class DirTool extends React.Component {
constructor(props) {
super(props);
this.state = {
isRepoTagDialogOpen: false,
isDropdownMenuOpen: false,
};
this.sortOptions = [
{ value: 'name-asc', text: gettext('By name ascending') },
{ value: 'name-desc', text: gettext('By name descending') },
{ value: 'time-asc', text: gettext('By time ascending') },
{ value: 'time-desc', text: gettext('By time descending') }
];
}
toggleDropdownMenu = () => {
this.setState({
isDropdownMenuOpen: !this.state.isDropdownMenuOpen
});
};
hidePopover = (e) => {
if (e) {
let dom = e.target;
while (dom) {
if (typeof dom.className === 'string' && dom.className.includes('tag-color-popover')) return;
dom = dom.parentNode;
}
}
this.setState({ isRepoTagDialogOpen: false });
};
toggleCancel = () => {
this.setState({ isRepoTagDialogOpen: false });
};
getMenu = () => {
const list = [];
const { userPerm, currentPath } = this.props;
if (userPerm !== 'rw' || Utils.isMarkdownFile(currentPath)) {
return list;
}
const { TAGS } = TextTranslation;
if (enableFileTags) {
list.push(TAGS);
}
return list;
};
onMenuItemClick = (item) => {
const { key } = item;
switch (key) {
case 'Tags':
this.setState({ isRepoTagDialogOpen: !this.state.isRepoTagDialogOpen });
break;
}
};
onMenuItemKeyDown = (e, item) => {
if (e.key == 'Enter' || e.key == 'Space') {
this.onMenuItemClick(item);
}
};
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.props.sortItems(sortBy, sortOrder);
};
showDirentDetail = () => {
this.props.switchViewMode(DIRENT_DETAIL_MODE);
};
render() {
const menuItems = this.getMenu();
const { isDropdownMenuOpen } = this.state;
const { repoID, currentMode, currentPath, sortBy, sortOrder, viewId, isCustomPermission } = this.props;
const { currentMode, currentPath, sortBy, sortOrder, viewId, isCustomPermission, onToggleDetail, onCloseDetail } = this.props;
const propertiesText = TextTranslation.PROPERTIES.value;
const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/');
const sortOptions = this.sortOptions.map(item => {
return {
...item,
isSelected: item.value === `${sortBy}-${sortOrder}`
};
});
const isTagView = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/');
const isAllTagsView = currentPath.split('/').pop() === ALL_TAGS_ID;
if (isFileExtended) {
return (
<div className="dir-tool">
<MetadataViewToolBar viewId={viewId} isCustomPermission={isCustomPermission} showDetail={this.showDirentDetail} />
<MetadataViewToolBar
viewId={viewId}
isCustomPermission={isCustomPermission}
onToggleDetail={onToggleDetail}
onCloseDetail={onCloseDetail}
/>
</div>
);
}
if (isTagView) {
return (
<div className="dir-tool">
{isAllTagsView && <TagsTableSearcher />}
{isAllTagsView ? <AllTagsSortSetter /> : <TagFilesSortSetter />}
</div>
);
}
return (
<React.Fragment>
<div className="dir-tool d-flex">
<ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} />
<ReposSortMenu sortOptions={sortOptions} onSelectSortOption={this.onSelectSortOption}/>
{(!isCustomPermission) &&
<div className="cur-view-path-btn" onClick={this.showDirentDetail}>
<span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span>
</div>
}
{menuItems.length > 0 &&
<Dropdown isOpen={isDropdownMenuOpen} toggle={this.toggleDropdownMenu}>
<DropdownToggle
tag="i"
id="cur-folder-more-op-toggle"
className='cur-view-path-btn sf3-font-more sf3-font'
data-toggle="dropdown"
title={gettext('More operations')}
aria-label={gettext('More operations')}
aria-expanded={isDropdownMenuOpen}
>
</DropdownToggle>
<DropdownMenu right={true}>
{menuItems.map((menuItem, index) => {
if (menuItem === 'Divider') {
return <DropdownItem key={index} divider />;
} else {
return (
<DropdownItem
key={index}
onClick={this.onMenuItemClick.bind(this, menuItem)}
onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)}
>{menuItem.value}
</DropdownItem>
);
}
})}
</DropdownMenu>
</Dropdown>
}
</div>
{this.state.isRepoTagDialogOpen &&
<SeahubPopover
popoverClassName="list-tag-popover"
target="cur-folder-more-op-toggle"
hideSeahubPopover={this.hidePopover}
hideSeahubPopoverWithEsc={this.hidePopover}
canHideSeahubPopover={true}
boundariesElement={document.body}
placement={'bottom-end'}
>
<ListTagPopover
repoID={repoID}
onListTagCancel={this.toggleCancel}
/>
</SeahubPopover>
<div className="dir-tool d-flex">
<ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} />
<SortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
{(!isCustomPermission) &&
<div className="cur-view-path-btn" onClick={onToggleDetail}>
<span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span>
</div>
}
</React.Fragment>
</div>
);
}

View File

@ -1,7 +1,8 @@
import React from 'react';
import { Popover } from 'reactstrap';
import PropTypes from 'prop-types';
import { KeyCodes } from '../../constants';
import { KeyCodes } from '../constants';
import { getEventClassName } from '../utils/dom';
const propTypes = {
target: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
@ -9,17 +10,17 @@ const propTypes = {
innerClassName: PropTypes.string,
popoverClassName: PropTypes.string,
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
hideSeahubPopover: PropTypes.func.isRequired,
hideSeahubPopoverWithEsc: PropTypes.func,
hidePopover: PropTypes.func.isRequired,
hidePopoverWithEsc: PropTypes.func,
hideArrow: PropTypes.bool,
canHideSeahubPopover: PropTypes.bool,
canHidePopover: PropTypes.bool,
placement: PropTypes.string,
modifiers: PropTypes.object
};
class SeahubPopover extends React.Component {
class CustomizePopover extends React.Component {
SeahubPopoverRef = null;
popoverRef = null;
isSelectOpen = false;
componentDidMount() {
@ -32,28 +33,23 @@ class SeahubPopover extends React.Component {
document.removeEventListener('keydown', this.onKeyDown);
}
getEventClassName = (e) => {
// svg mouseEvent event.target.className is an object
if (!e || !e.target) return '';
return e.target.getAttribute('class') || '';
};
onKeyDown = (e) => {
const { canHideSeahubPopover, hideSeahubPopoverWithEsc } = this.props;
if (e.keyCode === KeyCodes.Escape && typeof hideSeahubPopoverWithEsc === 'function' && !this.isSelectOpen) {
const { canHidePopover = true, hidePopoverWithEsc } = this.props;
if (e.keyCode === KeyCodes.Escape && typeof hidePopoverWithEsc === 'function' && !this.isSelectOpen) {
e.preventDefault();
hideSeahubPopoverWithEsc();
hidePopoverWithEsc();
} else if (e.keyCode === KeyCodes.Enter) {
// Resolve the default behavior of the enter key when entering formulas is blocked
if (canHideSeahubPopover) return;
if (canHidePopover) return;
e.stopImmediatePropagation();
}
};
onMouseDown = (e) => {
if (!this.props.canHideSeahubPopover) return;
if (this.SeahubPopoverRef && e && this.getEventClassName(e).indexOf('popover') === -1 && !this.SeahubPopoverRef.contains(e.target)) {
this.props.hideSeahubPopover(e);
const { canHidePopover = true } = this.props;
if (!canHidePopover) return;
if (this.popoverRef && e && getEventClassName(e).indexOf('popover') === -1 && !this.popoverRef.contains(e.target)) {
this.props.hidePopover(e);
}
};
@ -63,8 +59,8 @@ class SeahubPopover extends React.Component {
render() {
const {
target, boundariesElement, innerClassName, popoverClassName, hideArrow, modifiers,
placement,
target, boundariesElement, innerClassName, popoverClassName, hideArrow = true, modifiers,
placement = 'bottom-start',
} = this.props;
let additionalProps = {};
if (boundariesElement) {
@ -82,7 +78,7 @@ class SeahubPopover extends React.Component {
modifiers={modifiers}
{...additionalProps}
>
<div ref={ref => this.SeahubPopoverRef = ref} onClick={this.onPopoverInsideClick}>
<div ref={ref => this.popoverRef = ref} onClick={this.onPopoverInsideClick}>
{this.props.children}
</div>
</Popover>
@ -90,12 +86,6 @@ class SeahubPopover extends React.Component {
}
}
SeahubPopover.defaultProps = {
placement: 'bottom-start',
hideArrow: true,
canHideSeahubPopover: true
};
CustomizePopover.propTypes = propTypes;
SeahubPopover.propTypes = propTypes;
export default SeahubPopover;
export default CustomizePopover;

View File

@ -0,0 +1,100 @@
.seafile-customize-select {
position: relative;
display: flex;
padding: 0 10px;
border-radius: 3px;
align-items: center;
justify-content: space-between;
max-width: 900px;
user-select: none;
text-align: left;
line-height: 1.5;
background-image: none;
font-size: 14px;
color: #212529;
}
.seafile-customize-select:focus,
.seafile-customize-select.focus {
border-color: #1991eb !important;
box-shadow: 0 0 0 2px rgba(70, 127, 207, 0.25);
}
.seafile-customize-select.disabled:focus,
.seafile-customize-select.focus.disabled,
.seafile-customize-select.disabled:hover {
border-color: rgba(0, 40, 100, 0.12) !important;
box-shadow: unset;
cursor: default;
}
.seafile-customize-select:hover {
cursor: pointer;
border-color: rgb(179, 179, 179);
}
.seafile-customize-select .sf3-font-down {
color: #999;
}
.seafile-customize-select .selected-option {
display: flex;
flex: 1;
overflow: hidden;
flex-wrap: nowrap;
align-items: center;
justify-content: space-between;
background: #fff;
}
.seafile-customize-select .selected-option .custom-select-dropdown-icon {
height: 12px;
width: 12px;
color: #999;
display: flex;
align-items: center;
justify-content: center;
margin-left: 0.5rem;
}
.seafile-customize-select.selector-collaborator .seafile-option-group .seafile-option-group-content,
.seafile-customize-select.selector-group .seafile-option-group .seafile-option-group-content {
padding: 10px;
}
.seafile-customize-select.selector-collaborator .seafile-option-group .seafile-option-group-content {
padding: 10px 0;
}
.seafile-customize-select.selector-collaborator .option {
padding: 5px 0 5px 10px !important;
line-height: 20px;
}
.seafile-customize-select.selector-group .option {
height: 30px;
display: flex;
align-items: center;
}
.seafile-customize-select.selector-group .select-group-option {
justify-content: space-between;
}
.seafile-customize-select.selector-group .selected-option .selected-group {
padding: 0 2px;
background: #eceff4;
border-radius: 3px;
}
.seafile-customize-select .selected-option-show {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.seafile-customize-select .select-placeholder {
line-height: 1;
font-size: 14px;
white-space: nowrap;
}

View File

@ -0,0 +1,173 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import ModalPortal from '../modal-portal';
import SelectOptionGroup from './select-option-group';
import { getEventClassName } from '../../utils/dom';
import './index.css';
class CustomizeSelect extends Component {
constructor(props) {
super(props);
this.state = {
isShowSelectOptions: false
};
}
onSelectToggle = (event) => {
event.preventDefault();
/*
if select is showing, click events do not need to be monitored by other click events,
so it can be closed when other select is clicked.
*/
if (this.state.isShowSelectOptions) event.stopPropagation();
const eventClassName = getEventClassName(event);
if (this.props.readOnly || eventClassName.indexOf('option-search-control') > -1 || eventClassName === 'seafile-option-group-search') return;
// Prevent closing by pressing the space bar in the search input
if (event.target.value === '') return;
this.setState({
isShowSelectOptions: !this.state.isShowSelectOptions
});
};
onClick = (event) => {
if (this.props.isShowSelected && event.target.className.includes('icon-fork-number')) {
return;
}
if (!this.selector.contains(event.target)) {
this.closeSelect();
}
};
closeSelect = () => {
this.setState({ isShowSelectOptions: false });
};
getSelectedOptionTop = () => {
if (!this.selector) return 38;
const { height } = this.selector.getBoundingClientRect();
return height;
};
getFilterOptions = (searchValue) => {
const { options, searchable } = this.props;
if (!searchable) return options || [];
const validSearchVal = searchValue.trim().toLowerCase();
if (!validSearchVal) return options || [];
return options.filter(option => {
const { value, name } = option;
if (typeof name === 'string') {
return name.toLowerCase().indexOf(validSearchVal) > -1;
}
if (typeof value === 'object') {
if (value.column) {
return value.column.name.toLowerCase().indexOf(validSearchVal) > -1;
}
if (value.name) {
return value.name.toLowerCase().indexOf(validSearchVal) > -1;
}
return value.columnOption && value.columnOption.name.toLowerCase().indexOf(validSearchVal) > -1;
}
return false;
});
};
renderDropDownIcon = () => {
const { readOnly, component } = this.props;
if (readOnly) return;
const { DropDownIcon } = component || {};
if (DropDownIcon) {
return (
<div className="custom-select-dropdown-icon">{DropDownIcon}</div>
);
}
return (<i className="sf3-font sf3-font-down" aria-hidden="true"></i>);
};
render() {
const { className, value, options, placeholder, searchable, searchPlaceholder, noOptionsPlaceholder,
readOnly, isInModal, addOptionAble, component } = this.props;
return (
<div
ref={(node) => this.selector = node}
className={classnames('seafile-customize-select custom-select',
{ 'focus': this.state.isShowSelectOptions },
{ 'disabled': readOnly },
className
)}
onClick={this.onSelectToggle}>
<div className="selected-option">
{value && value.label ?
<span className="selected-option-show">{value.label}</span>
:
<span className="select-placeholder">{placeholder}</span>
}
{this.renderDropDownIcon()}
</div>
{this.state.isShowSelectOptions && !isInModal && (
<SelectOptionGroup
value={value}
addOptionAble={addOptionAble}
component={component}
isShowSelected={this.props.isShowSelected}
top={this.getSelectedOptionTop()}
options={options}
onSelectOption={this.props.onSelectOption}
searchable={searchable}
searchPlaceholder={searchPlaceholder}
noOptionsPlaceholder={noOptionsPlaceholder}
onClickOutside={this.onClick}
closeSelect={this.closeSelect}
getFilterOptions={this.getFilterOptions}
supportMultipleSelect={this.props.supportMultipleSelect}
/>
)}
{this.state.isShowSelectOptions && isInModal && (
<ModalPortal>
<SelectOptionGroup
className={className}
value={value}
addOptionAble={addOptionAble}
component={component}
isShowSelected={this.props.isShowSelected}
position={this.selector.getBoundingClientRect()}
isInModal={isInModal}
top={this.getSelectedOptionTop()}
options={options}
onSelectOption={this.props.onSelectOption}
searchable={searchable}
searchPlaceholder={searchPlaceholder}
noOptionsPlaceholder={noOptionsPlaceholder}
onClickOutside={this.onClick}
closeSelect={this.closeSelect}
getFilterOptions={this.getFilterOptions}
supportMultipleSelect={this.props.supportMultipleSelect}
/>
</ModalPortal>
)}
</div>
);
}
}
CustomizeSelect.propTypes = {
className: PropTypes.string,
value: PropTypes.object,
options: PropTypes.array,
placeholder: PropTypes.string,
onSelectOption: PropTypes.func,
readOnly: PropTypes.bool,
searchable: PropTypes.bool,
addOptionAble: PropTypes.bool,
searchPlaceholder: PropTypes.string,
noOptionsPlaceholder: PropTypes.string,
component: PropTypes.object,
supportMultipleSelect: PropTypes.bool,
isShowSelected: PropTypes.bool,
isInModal: PropTypes.bool, // if select component in a modal (option group need ModalPortal to show)
};
export default CustomizeSelect;

View File

@ -0,0 +1,103 @@
.seafile-option-group {
position: absolute;
left: 0;
min-height: 60px;
max-height: 300px;
min-width: 100%;
max-width: 15rem;
padding: 0.5rem 0;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
background: #fff;
border: 1px solid rgba(0, 40, 100, 0.12);
border-radius: 3px;
z-index: 10001;
}
.seafile-option-group .seafile-option-group-search {
width: 100%;
padding: 0 10px 6px 10px;
min-width: 170px;
}
.seafile-option-group-search .form-control {
height: 31px;
}
.seafile-option-group .none-search-result {
height: 100px;
width: 100%;
padding: 10px;
color: #666666;
}
.seafile-option-group .seafile-option-group-content {
max-height: 252px;
overflow-y: auto;
}
.seafile-select-option {
display: block;
width: 100%;
line-height: 24px;
padding: 0.25rem 10px;
clear: both;
font-weight: 400;
text-align: inherit;
background-color: transparent;
border: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.seafile-select-option.seafile-select-option-active {
background-color: #20a0ff;
color: #fff;
cursor: pointer;
}
.seafile-select-option.seafile-select-option-active .select-option-name {
color: #fff;
}
.seafile-select-option:hover .header-icon .seafile-multicolor-icon,
.seafile-select-option.seafile-select-option-active .header-icon .seafile-multicolor-icon {
fill: #fff;
}
.seafile-select-option:not(.seafile-select-option-active):hover .header-icon .seafile-multicolor-icon {
fill: #aaa;
}
.seafile-select-option .select-option-name .single-select-option {
margin: 0 0 0 12px;
}
.seafile-select-option .select-option-name .multiple-select-option {
margin: 0;
}
.seafile-option-group-selector-single-select .select-option-name,
.seafile-option-group-selector-multiple-select .multiple-option-name {
display: flex;
align-items: center;
justify-content: space-between;
}
.seafile-option-group-selector-multiple-select .multiple-check-icon {
display: inline-flex;
width: 20px;
text-align: center;
}
.seafile-option-group-selector-multiple-select .multiple-check-icon .seafile-multicolor-icon-check-mark {
font-size: 12px;
color: #798d99;
}
.seafile-option-group-selector-single-select .seafile-select-option:hover,
.seafile-option-group-selector-single-select .seafile-select-option.seafile-select-option-active,
.seafile-option-group-selector-multiple-select .seafile-select-option:hover,
.seafile-option-group-selector-multiple-select .seafile-select-option.seafile-select-option-active {
background-color: #f5f5f5;
}

View File

@ -0,0 +1,233 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import ClickOutside from '../../click-outside';
import SearchInput from '../../search-input';
import Option from './option';
import { KeyCodes } from '../../../constants';
import './index.css';
const OPTION_HEIGHT = 32;
class SelectOptionGroup extends Component {
constructor(props) {
super(props);
this.state = {
searchVal: '',
activeIndex: -1,
disableHover: false,
};
this.filterOptions = null;
this.timer = null;
}
componentDidMount() {
window.addEventListener('keydown', this.onHotKey);
setTimeout(() => {
this.resetMenuStyle();
}, 1);
}
componentWillUnmount() {
this.filterOptions = null;
this.timer && clearTimeout(this.timer);
window.removeEventListener('keydown', this.onHotKey);
}
resetMenuStyle = () => {
const { isInModal, position } = this.props;
const { top, height } = this.optionGroupRef.getBoundingClientRect();
if (isInModal) {
if (position.y + position.height + height > window.innerHeight) {
this.optionGroupRef.style.top = (position.y - height) + 'px';
}
this.optionGroupRef.style.opacity = 1;
}
else {
if (height + top > window.innerHeight) {
const borderWidth = 2;
this.optionGroupRef.style.top = -1 * (height + borderWidth) + 'px';
}
}
};
onHotKey = (event) => {
const keyCode = event.keyCode;
if (keyCode === KeyCodes.UpArrow) {
this.onPressUp();
} else if (keyCode === KeyCodes.DownArrow) {
this.onPressDown();
} else if (keyCode === KeyCodes.Enter) {
let option = this.filterOptions && this.filterOptions[this.state.activeIndex];
if (option) {
this.props.onSelectOption(option.value);
if (!this.props.supportMultipleSelect) {
this.props.closeSelect();
}
}
} else if (keyCode === KeyCodes.Tab || keyCode === KeyCodes.Escape) {
this.props.closeSelect();
}
};
onPressUp = () => {
if (this.state.activeIndex > 0) {
this.setState({ activeIndex: this.state.activeIndex - 1 }, () => {
this.scrollContent();
});
}
};
onPressDown = () => {
if (this.filterOptions && this.state.activeIndex < this.filterOptions.length - 1) {
this.setState({ activeIndex: this.state.activeIndex + 1 }, () => {
this.scrollContent();
});
}
};
onMouseDown = (e) => {
const { isInModal } = this.props;
// prevent event propagation when click option or search input
if (isInModal) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
};
scrollContent = () => {
const { offsetHeight, scrollTop } = this.optionGroupContentRef;
this.setState({ disableHover: true });
this.timer = setTimeout(() => {
this.setState({ disableHover: false });
}, 500);
if (this.state.activeIndex * OPTION_HEIGHT === 0) {
this.optionGroupContentRef.scrollTop = 0;
return;
}
if (this.state.activeIndex * OPTION_HEIGHT < scrollTop) {
this.optionGroupContentRef.scrollTop = scrollTop - OPTION_HEIGHT;
}
else if (this.state.activeIndex * OPTION_HEIGHT > offsetHeight + scrollTop) {
this.optionGroupContentRef.scrollTop = scrollTop + OPTION_HEIGHT;
}
};
changeIndex = (index) => {
this.setState({ activeIndex: index });
};
onChangeSearch = (searchVal) => {
let value = searchVal || '';
if (value !== this.state.searchVal) {
this.setState({ searchVal: value, activeIndex: -1, });
}
};
renderOptGroup = (searchVal) => {
let { noOptionsPlaceholder, onSelectOption } = this.props;
this.filterOptions = this.props.getFilterOptions(searchVal);
if (this.filterOptions.length === 0) {
return (
<div className="none-search-result">{noOptionsPlaceholder}</div>
);
}
return this.filterOptions.map((opt, i) => {
let key = opt.value.column ? opt.value.column.key : i;
let isActive = this.state.activeIndex === i;
return (
<Option
key={key}
index={i}
isActive={isActive}
value={opt.value}
onSelectOption={onSelectOption}
changeIndex={this.changeIndex}
supportMultipleSelect={this.props.supportMultipleSelect}
disableHover={this.state.disableHover}
>
{opt.label}
</Option>
);
});
};
render() {
const { searchable, searchPlaceholder, top, left, minWidth, value, isShowSelected, isInModal, position,
className, addOptionAble, component } = this.props;
const { AddOption } = component || {};
let { searchVal } = this.state;
let style = { top: top || 0, left: left || 0 };
if (minWidth) {
style = { top: top || 0, left: left || 0, minWidth };
}
if (isInModal) {
style = {
position: 'fixed',
left: position.x,
top: position.y + position.height,
minWidth: position.width,
opacity: 0,
};
}
return (
<ClickOutside onClickOutside={this.props.onClickOutside}>
<div
className={classnames('seafile-option-group', className ? 'seafile-option-group-' + className : '', {
'pt-0': isShowSelected,
'create-new-seafile-option-group': addOptionAble,
})}
ref={(ref) => this.optionGroupRef = ref}
style={style}
onMouseDown={this.onMouseDown}
>
{isShowSelected &&
<div className="editor-list-delete mb-2" onClick={(e) => e.stopPropagation()}>{value.label || ''}</div>
}
{searchable && (
<div className="seafile-option-group-search">
<SearchInput
className="option-search-control"
placeholder={searchPlaceholder}
onChange={this.onChangeSearch}
autoFocus={true}
/>
</div>
)}
<div className="seafile-option-group-content" ref={(ref) => this.optionGroupContentRef = ref}>
{this.renderOptGroup(searchVal)}
</div>
{addOptionAble && AddOption}
</div>
</ClickOutside>
);
}
}
SelectOptionGroup.propTypes = {
top: PropTypes.number,
left: PropTypes.number,
minWidth: PropTypes.number,
options: PropTypes.array,
onSelectOption: PropTypes.func,
searchable: PropTypes.bool,
addOptionAble: PropTypes.bool,
component: PropTypes.object,
searchPlaceholder: PropTypes.string,
noOptionsPlaceholder: PropTypes.string,
onClickOutside: PropTypes.func.isRequired,
closeSelect: PropTypes.func.isRequired,
getFilterOptions: PropTypes.func.isRequired,
supportMultipleSelect: PropTypes.bool,
value: PropTypes.object,
isShowSelected: PropTypes.bool,
stopClickEvent: PropTypes.bool,
isInModal: PropTypes.bool,
position: PropTypes.object,
className: PropTypes.string,
};
export default SelectOptionGroup;

View File

@ -0,0 +1,50 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
class Option extends Component {
onSelectOption = (value, event) => {
if (this.props.supportMultipleSelect) {
event.stopPropagation();
}
this.props.onSelectOption(value, event);
};
onMouseEnter = () => {
if (!this.props.disableHover) {
this.props.changeIndex(this.props.index);
}
};
onMouseLeave = () => {
if (!this.props.disableHover) {
this.props.changeIndex(-1);
}
};
render() {
return (
<div
className={classnames('seafile-select-option', { 'seafile-select-option-active': this.props.isActive })}
onClick={this.onSelectOption.bind(this, this.props.value)}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
>{this.props.children}
</div>
);
}
}
Option.propTypes = {
index: PropTypes.number,
isActive: PropTypes.bool,
changeIndex: PropTypes.func,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
onSelectOption: PropTypes.func,
supportMultipleSelect: PropTypes.bool,
disableHover: PropTypes.bool,
};
export default Option;

View File

@ -89,7 +89,7 @@ Picker.propTypes = {
showHourAndMinute: PropTypes.bool.isRequired,
disabledDate: PropTypes.func.isRequired,
value: PropTypes.object,
disabled: PropTypes.func.isRequired,
disabled: PropTypes.func,
inputWidth: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired
};

View File

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalBody } from 'reactstrap';
import { gettext, lang, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle, seafileVersion, additionalAboutDialogLinks, aboutDialogCustomHtml } from '../../utils/constants';
import '../../css/seahub-modal-header.css';
const propTypes = {
onCloseAboutDialog: PropTypes.func.isRequired,
@ -27,7 +28,11 @@ class AboutDialog extends React.Component {
return (
<Modal isOpen={true} toggle={toggleDialog}>
<ModalBody>
<button type="button" className="close" onClick={toggleDialog}><span aria-hidden="true">×</span></button>
<button type="button" className="close seahub-modal-btn p-0" aria-label={gettext('Close')} onClick={toggleDialog}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
<div className="about-content" dangerouslySetInnerHTML={{ __html: aboutDialogCustomHtml }}></div>
</ModalBody>
</Modal>
@ -36,7 +41,11 @@ class AboutDialog extends React.Component {
return (
<Modal isOpen={true} toggle={toggleDialog}>
<ModalBody>
<button type="button" className="close" onClick={toggleDialog}><span aria-hidden="true">×</span></button>
<button type="button" className="close seahub-modal-btn p-0" aria-label={gettext('Close')} onClick={toggleDialog}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
<div className="about-content">
<p><img src={mediaUrl + logoPath} height={logoHeight} width={logoWidth} title={siteTitle} alt="logo" /></p>
<p>{gettext('Server Version: ')}{seafileVersion}<br />© {(new Date()).getFullYear()} {gettext('Seafile')}</p>

View File

@ -1,9 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Form, FormGroup, Label, Input, Modal, ModalHeader, ModalBody, ModalFooter, Alert } from 'reactstrap';
import { Button, Form, FormGroup, Label, Input, Modal, ModalBody, ModalFooter, Alert } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { SeahubSelect } from '../common/select';
import { seafileAPI } from '../../utils/seafile-api';
import toaster from '../toast';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
sharedToken: PropTypes.string.isRequired,
@ -23,6 +25,12 @@ class AddAbuseReportDialog extends React.Component {
reporter: this.props.contactEmail,
errMessage: '',
};
this.typeOptions = [
{ value: 'copyright', label: gettext('Copyright Infringement') },
{ value: 'virus', label: gettext('Virus') },
{ value: 'abuse_content', label: gettext('Abuse Content') },
{ value: 'other', label: gettext('Other') },
];
}
onAbuseReport = () => {
@ -44,8 +52,8 @@ class AddAbuseReportDialog extends React.Component {
});
};
onAbuseTypeChange = (event) => {
let type = event.target.value;
onAbuseTypeChange = (option) => {
let type = option.value;
if (type === this.state.abuseType) {
return;
}
@ -65,25 +73,34 @@ class AddAbuseReportDialog extends React.Component {
render() {
return (
<Modal isOpen={this.props.isAddAbuseReportDialogOpen} toggle={this.props.toggleAddAbuseReportDialog}>
<ModalHeader toggle={this.props.toggleAddAbuseReportDialog}>{gettext('Report Abuse')}</ModalHeader>
<SeahubModalHeader toggle={this.props.toggleAddAbuseReportDialog}>{gettext('Report Abuse')}</SeahubModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="abuse-type-select">{gettext('Abuse Type')}</Label>
<Input type="select" id="abuse-type-select" onChange={(event) => this.onAbuseTypeChange(event)}>
<option value='copyright'>{gettext('Copyright Infringement')}</option>
<option value='virus'>{gettext('Virus')}</option>
<option value='abuse_content'>{gettext('Abuse Content')}</option>
<option value='other'>{gettext('Other')}</option>
</Input>
<Label>{gettext('Abuse Type')}</Label>
<SeahubSelect
options={this.typeOptions}
value={this.typeOptions.find(option => option.value === this.state.abuseType) || this.typeOptions[0]}
onChange={this.onAbuseTypeChange}
isClearable={false}
/>
</FormGroup>
<FormGroup>
<Label>{gettext('Contact Information')}</Label>
<Input type="text" value={this.state.reporter} onChange={(event) => this.setReporter(event)}/>
<Input
name="abuse-report-contact-information"
type="text"
value={this.state.reporter}
onChange={(event) => this.setReporter(event)}
/>
</FormGroup>
<FormGroup>
<Label>{gettext('Description')}</Label>
<Input type="textarea" onChange={(event) => this.setDescription(event)}/>
<Input
name="abuse-report-description"
type="textarea"
onChange={(event) => this.setDescription(event)}
/>
</FormGroup>
</Form>
{this.state.errMessage && <Alert color="danger">{this.state.errMessage}</Alert>}

View File

@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Label } from 'reactstrap';
import { Button, Modal, ModalBody, ModalFooter, Input, Label } from 'reactstrap';
import SeahubModalHeader from '../common/seahub-modal-header';
import { gettext, isPro } from '../../utils/constants';
import wikiAPI from '../../utils/wiki-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import { SeahubSelect, NoOptionsStyle } from '../common/select';
import { SeahubSelect } from '../common/select';
const propTypes = {
toggleCancel: PropTypes.func.isRequired,
@ -82,10 +83,16 @@ class AddWikiDialog extends React.Component {
render() {
return (
<Modal isOpen={true} autoFocus={false} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Add Wiki')}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{gettext('Add Wiki')}</SeahubModalHeader>
<ModalBody>
<Label>{gettext('Name')}</Label>
<Input onKeyDown={this.handleKeyDown} autoFocus={true} value={this.state.name} onChange={this.inputNewName}/>
<Input
onKeyDown={this.handleKeyDown}
autoFocus={true}
value={this.state.name}
onChange={this.inputNewName}
name="wiki-name"
/>
{isPro &&
<>
<Label className='mt-4'>{gettext('Wiki owner')} ({gettext('Optional')})</Label>
@ -96,9 +103,6 @@ class AddWikiDialog extends React.Component {
placeholder={gettext('Select a department')}
maxMenuHeight={200}
value={this.state.selectedOption}
components={{ NoOptionsMessage: (
<div style={NoOptionsStyle}>{gettext('No department')}</div>
) }}
noOptionsMessage={() => {return gettext('No options available');}}
/>
</>

View File

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { Button, Modal, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
wikiPageName: PropTypes.string,
@ -54,13 +55,14 @@ class AddWikiPageDialog extends React.Component {
const { handleClose } = this.props;
return (
<Modal isOpen={true} toggle={handleClose} onOpened={this.onDialogLoad}>
<ModalHeader toggle={handleClose}>{gettext('New page')}</ModalHeader>
<SeahubModalHeader toggle={handleClose}>{gettext('New page')}</SeahubModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="pageName">{gettext('Name')}</Label>
<Input
id="pageName"
name="wiki-page-name"
onKeyDown={this.handleKeyDown}
innerRef={this.inputRef}
value={this.state.wikiPageName}

View File

@ -1,8 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalFooter, ModalBody } from 'reactstrap';
import { Button, Modal, ModalFooter, ModalBody } from 'reactstrap';
import { Utils } from '../../utils/utils';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
groupName: PropTypes.string.isRequired,
@ -29,9 +30,9 @@ class ChangeGroupDialog extends React.Component {
const msg = gettext('Are you sure to change group {placeholder} to department ?').replace('{placeholder}', groupName);
return (
<Modal isOpen={true} toggle={this.props.toggleDialog}>
<ModalHeader toggle={this.props.toggleDialog}>
<SeahubModalHeader toggle={this.props.toggleDialog}>
{gettext('Change group to department')}
</ModalHeader>
</SeahubModalHeader>
<ModalBody>
<p dangerouslySetInnerHTML={{ __html: msg }}></p>
</ModalBody>

View File

@ -1,10 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Alert } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Alert } from 'reactstrap';
import { gettext, repoPasswordMinLength } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api';
import toaster from '../toast';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
repoID: PropTypes.string.isRequired,
@ -100,9 +101,9 @@ class ChangeRepoPasswordDialog extends React.Component {
return (
<Modal isOpen={true} style={{ height: 'auto' }} toggle={toggleDialog}>
<ModalHeader toggle={toggleDialog}>
<SeahubModalHeader toggle={toggleDialog}>
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
</ModalHeader>
</SeahubModalHeader>
<ModalBody>
<form id="repo-change-passwd-form" action="" method="post">
<label htmlFor="passwd">{gettext('Old Password')}</label>

View File

@ -1,12 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import CreatableSelect from 'react-select/creatable';
import { MenuSelectStyle } from '../common/select/seahub-select-style';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
repoID: PropTypes.string.isRequired,
@ -60,7 +61,7 @@ class CleanTrash extends React.Component {
const { formErrorMsg } = this.state;
return (
<Modal isOpen={true} toggle={this.props.toggleDialog}>
<ModalHeader toggle={this.props.toggleDialog}>{gettext('Clean')}</ModalHeader>
<SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('Clean')}</SeahubModalHeader>
<ModalBody>
<React.Fragment>
<p>{gettext('Clear files in trash and history')}</p>

View File

@ -1,11 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import { Modal, ModalBody } from 'reactstrap';
import dayjs from 'dayjs';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import Loading from '../loading';
import Icon from '../icon';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
import '../../css/commit-details.css';
@ -45,10 +47,13 @@ class CommitDetails extends React.Component {
render() {
const { toggleDialog, commitTime } = this.props;
return (
<Modal isOpen={true} centered={true} toggle={toggleDialog}>
<ModalHeader toggle={toggleDialog}>{gettext('Modification Details')}</ModalHeader>
<Modal isOpen={true} toggle={toggleDialog}>
<SeahubModalHeader toggle={toggleDialog}>{gettext('Modification Details')}</SeahubModalHeader>
<ModalBody>
<p className="small">{dayjs(commitTime).format('YYYY-MM-DD HH:mm:ss')}</p>
<p className="repo-commit-time mb-6 d-flex align-items-center">
<Icon symbol="time" className="mr-1" />
{dayjs(commitTime).format('YYYY-MM-DD HH:mm:ss')}
</p>
<Content data={this.state} />
</ModalBody>
</Modal>
@ -87,11 +92,11 @@ class Content extends React.Component {
}
return (
<React.Fragment key={index}>
<h6>{item.title}</h6>
<ul>
<h6 className="mt-4">{item.title}</h6>
<ul className="list-unstyled">
{
data[item.type].map((item, index) => {
return <li key={index} dangerouslySetInnerHTML={{ __html: item }} className="commit-detail-item text-truncate"></li>;
return <li key={index} dangerouslySetInnerHTML={{ __html: item }} className="text-truncate"></li>;
})
}
</ul>

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
title: PropTypes.string.isRequired,
@ -26,7 +27,7 @@ class CommonOperationConfirmationDialog extends Component {
let { title, message, confirmBtnText } = this.props;
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{title}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{title}</SeahubModalHeader>
<ModalBody>
<p dangerouslySetInnerHTML={{ __html: message }}></p>
</ModalBody>

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
formActionURL: PropTypes.string.isRequired,
@ -24,7 +25,7 @@ class ConfirmDeleteAccount extends Component {
const { formActionURL, csrfToken, toggle } = this.props;
return (
<Modal centered={true} isOpen={true} toggle={toggle}>
<ModalHeader toggle={toggle}>{gettext('Delete Account')}</ModalHeader>
<SeahubModalHeader toggle={toggle}>{gettext('Delete Account')}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Really want to delete your account?')}</p>
<form ref={this.form} className="d-none" method="post" action={formActionURL}>

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
formActionURL: PropTypes.string.isRequired,
@ -24,7 +25,7 @@ class ConfirmDisconnectDingtalk extends Component {
const { formActionURL, csrfToken, toggle } = this.props;
return (
<Modal centered={true} isOpen={true} toggle={toggle}>
<ModalHeader toggle={toggle}>{gettext('Disconnect')}</ModalHeader>
<SeahubModalHeader toggle={toggle}>{gettext('Disconnect')}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Are you sure you want to disconnect?')}</p>
<form ref={this.form} className="d-none" method="post" action={formActionURL}>

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
formActionURL: PropTypes.string.isRequired,
@ -24,7 +25,7 @@ class ConfirmDisconnectWechat extends Component {
const { formActionURL, csrfToken, toggle } = this.props;
return (
<Modal centered={true} isOpen={true} toggle={toggle}>
<ModalHeader toggle={toggle}>{gettext('Disconnect')}</ModalHeader>
<SeahubModalHeader toggle={toggle}>{gettext('Disconnect')}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Are you sure you want to disconnect?')}</p>
<form ref={this.form} className="d-none" method="post" action={formActionURL}>

View File

@ -0,0 +1,46 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
formActionURL: PropTypes.string.isRequired,
csrfToken: PropTypes.string.isRequired,
toggle: PropTypes.func.isRequired
};
class ConfirmDisconnectWeixin extends Component {
constructor(props) {
super(props);
this.form = React.createRef();
}
disconnect = () => {
this.form.current.submit();
};
render() {
const { formActionURL, csrfToken, toggle } = this.props;
return (
<Modal centered={true} isOpen={true} toggle={toggle}>
<SeahubModalHeader toggle={toggle}>{gettext('Disconnect')}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Are you sure you want to disconnect?')}</p>
<form ref={this.form} className="d-none" method="post" action={formActionURL}>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
</form>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={toggle}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.disconnect}>{gettext('Disconnect')}</Button>
</ModalFooter>
</Modal>
);
}
}
ConfirmDisconnectWeixin.propTypes = propTypes;
export default ConfirmDisconnectWeixin;

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
restoreRepo: PropTypes.func.isRequired,
@ -28,7 +29,7 @@ class ConfirmRestoreRepo extends Component {
const { toggle } = this.props;
return (
<Modal centered={true} isOpen={true} toggle={toggle}>
<ModalHeader toggle={toggle}>{gettext('Restore Library')}</ModalHeader>
<SeahubModalHeader toggle={toggle}>{gettext('Restore Library')}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Are you sure you want to restore this library?')}</p>
</ModalBody>

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button, FormGroup, Label, Input } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button, FormGroup, Label, Input } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
executeOperation: PropTypes.func.isRequired,
@ -35,7 +36,7 @@ class ConfirmUnlinkDevice extends Component {
render() {
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Unlink device')}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{gettext('Unlink device')}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Are you sure you want to unlink this device?')}</p>
<FormGroup check>

View File

@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Label } from 'reactstrap';
import { Button, Modal, ModalBody, ModalFooter, Input, Label } from 'reactstrap';
import { gettext, isPro } from '../../utils/constants';
import wikiAPI from '../../utils/wiki-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import { SeahubSelect, NoOptionsStyle } from '../common/select';
import { SeahubSelect } from '../common/select';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
toggleCancel: PropTypes.func.isRequired,
@ -78,10 +79,16 @@ class ConvertWikiDialog extends React.Component {
render() {
return (
<Modal isOpen={true} autoFocus={false} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Convert Wiki')}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{gettext('Convert Wiki')}</SeahubModalHeader>
<ModalBody>
<Label>{gettext('Name')}</Label>
<Input onKeyDown={this.handleKeyDown} autoFocus={true} value={this.state.name} onChange={this.inputNewName}/>
<Input
name="wiki-name"
onKeyDown={this.handleKeyDown}
autoFocus={true}
value={this.state.name}
onChange={this.inputNewName}
/>
{isPro &&
<>
<Label className='mt-4'>{gettext('Wiki owner')} ({gettext('Optional')})</Label>
@ -92,9 +99,6 @@ class ConvertWikiDialog extends React.Component {
placeholder={gettext('Select a department')}
maxMenuHeight={200}
value={this.state.selectedOption}
components={{ NoOptionsMessage: (
<div style={NoOptionsStyle}>{gettext('No department')}</div>
) }}
noOptionsMessage={() => {return gettext('No options available');}}
/>
</>

View File

@ -1,9 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalFooter, ModalBody, Alert, Row, Col } from 'reactstrap';
import FileChooser from '../file-chooser';
import { gettext } from '../../utils/constants';
import { Modal, ModalHeader } from 'reactstrap';
import Searcher from '../file-chooser/searcher';
import SelectDirentBody from './select-dirent-body';
import { MODE_TYPE_MAP } from '../../constants';
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, isPro } from '../../utils/constants';
import { RepoInfo } from '../../models';
import toaster from '../toast';
const propTypes = {
path: PropTypes.string.isRequired,
@ -15,21 +20,70 @@ const propTypes = {
onItemsCopy: PropTypes.func,
onCancelCopy: PropTypes.func.isRequired,
repoEncrypted: PropTypes.bool.isRequired,
onAddFolder: PropTypes.func,
};
// need dirent file Path
class CopyDirent extends React.Component {
constructor(props) {
super(props);
this.state = {
repo: { repo_id: this.props.repoID },
mode: MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY,
currentRepo: { repo_id: this.props.repoID },
selectedRepo: { repo_id: this.props.repoID },
repoList: [],
selectedPath: this.props.path,
selectedSearchedRepo: null,
selectedSearchedItem: { repoID: '', filePath: '' },
searchStatus: '',
searchResults: [],
showSearchBar: false,
errMessage: '',
mode: 'only_current_library',
initToShowChildren: false,
};
this.lastMode = MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY;
}
componentDidMount() {
this.initialize();
}
initialize = async () => {
try {
const res = await seafileAPI.getRepoInfo(this.props.repoID);
const repo = new RepoInfo(res.data);
this.setState({ currentRepo: repo });
await this.fetchRepoList();
} catch (error) {
const errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
}
};
fetchRepoList = async () => {
try {
const res = await seafileAPI.listRepos();
const repos = res.data.repos;
const repoList = [];
const uniqueRepoIds = new Set();
for (const repo of repos) {
if (repo.permission === 'rw' && repo.repo_id !== this.props.repoID && !uniqueRepoIds.has(repo.repo_id)) {
uniqueRepoIds.add(repo.repo_id);
repoList.push(repo);
}
}
const sortedRepoList = Utils.sortRepos(repoList, 'name', 'asc');
const selectedRepo = sortedRepoList.find((repo) => repo.repo_id === this.props.repoID);
this.setState({
repoList: sortedRepoList,
repo: selectedRepo,
});
} catch (error) {
const errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
}
};
handleSubmit = () => {
if (this.props.isMultipleOperation) {
this.copyItems();
@ -39,10 +93,10 @@ class CopyDirent extends React.Component {
};
copyItems = () => {
let { repo, selectedPath } = this.state;
let { selectedRepo, selectedPath } = this.state;
let message = gettext('Invalid destination path');
if (!repo || selectedPath === '') {
if (!selectedRepo || selectedPath === '') {
this.setState({ errMessage: message });
return;
}
@ -78,16 +132,16 @@ class CopyDirent extends React.Component {
return;
}
this.props.onItemsCopy(repo, selectedPath);
this.props.onItemsCopy(selectedRepo, selectedPath, true);
this.toggle();
};
copyItem = () => {
let { repo, repoID, selectedPath } = this.state;
let { repoID, selectedRepo, selectedPath } = this.state;
let direntPath = Utils.joinPath(this.props.path, this.props.dirent.name);
let message = gettext('Invalid destination path');
if (!repo || (repo.repo_id === repoID && selectedPath === '')) {
if (!selectedRepo || (selectedRepo.repo_id === repoID && selectedPath === '')) {
this.setState({ errMessage: message });
return;
}
@ -107,7 +161,7 @@ class CopyDirent extends React.Component {
return;
}
this.props.onItemCopy(repo, this.props.dirent, selectedPath, this.props.path);
this.props.onItemCopy(selectedRepo, this.props.dirent, selectedPath, this.props.path, true);
this.toggle();
};
@ -115,9 +169,122 @@ class CopyDirent extends React.Component {
this.props.onCancelCopy();
};
selectRepo = (repo) => {
this.setState({ selectedRepo: repo });
};
selectSearchedRepo = (repo) => {
this.setState({ selectedSearchedRepo: repo });
};
setSelectedPath = (selectedPath) => {
this.setState({ selectedPath });
};
setErrMessage = (message) => {
this.setState({ errMessage: message });
};
updateMode = (mode) => {
if (mode === this.state.mode) return;
if (mode !== MODE_TYPE_MAP.SEARCH_RESULTS) {
this.lastMode = mode;
}
const isShowChildren = mode === MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY || mode === MODE_TYPE_MAP.SEARCH_RESULTS;
this.setState({
mode,
initToShowChildren: isShowChildren,
});
if (this.state.mode === MODE_TYPE_MAP.SEARCH_RESULTS) {
this.setState({
selectedSearchedRepo: null,
searchResults: [],
showSearchBar: false,
});
}
if (this.state.selectedSearchedRepo && mode !== MODE_TYPE_MAP.SEARCH_RESULTS) {
this.setState({
selectedSearchedRepo: null,
searchResults: [],
showSearchBar: false,
});
}
this.setState({ selectedSearchedItem: { repoID: '', filePath: '' } });
};
onUpdateSearchStatus = (status) => {
this.setState({ searchStatus: status });
};
onUpdateSearchResults = (results) => {
if (results.length > 0) {
const firstResult = results[0];
this.setState({
selectedRepo: new RepoInfo(firstResult),
selectedPath: firstResult.path
});
}
this.setState({ searchResults: results });
};
onOpenSearchBar = () => {
this.setState({ showSearchBar: true });
};
onCloseSearchBar = () => {
const mode = this.lastMode;
this.setState({
mode,
searchStatus: '',
searchResults: [],
selectedSearchedRepo: null,
showSearchBar: false,
selectedPath: this.props.path,
initToShowChildren: mode === MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY,
});
};
onSearchedItemClick = (item) => {
item['type'] = item.is_dir ? 'dir' : 'file';
let repo = new RepoInfo(item);
this.onDirentItemClick(repo, item.path, item);
};
onSearchedItemDoubleClick = (item) => {
if (item.type !== 'dir') return;
seafileAPI.getRepoInfo(item.repo_id).then(res => {
const repoInfo = new RepoInfo(res.data);
const path = item.path.substring(0, item.path.length - 1);
const mode = item.repo_id === this.props.repoID ? MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY : MODE_TYPE_MAP.ONLY_OTHER_LIBRARIES;
this.lastMode = mode;
this.setState({
mode,
selectedRepo: repoInfo,
selectedSearchedRepo: repoInfo,
selectedPath: path,
selectedSearchedItem: { repoID: item.repo_id, filePath: path },
showSearchBar: mode === MODE_TYPE_MAP.ONLY_OTHER_LIBRARIES,
initToShowChildren: true,
});
}).catch(err => {
const errMessage = Utils.getErrorMsg(err);
toaster.danger(errMessage);
});
};
selectSearchedItem = (item) => {
this.setState({ selectedSearchedItem: item });
};
onDirentItemClick = (repo, selectedPath) => {
this.setState({
repo: repo,
selectedRepo: repo,
selectedPath: selectedPath,
errMessage: ''
});
@ -131,10 +298,6 @@ class CopyDirent extends React.Component {
});
};
onSelectedMode = (mode) => {
this.setState({ mode: mode });
};
renderTitle = () => {
const { dirent, isMultipleOperation } = this.props;
let title = gettext('Copy {placeholder} to');
@ -147,48 +310,69 @@ class CopyDirent extends React.Component {
};
render() {
const { dirent, selectedDirentList, isMultipleOperation, repoID, path } = this.props;
const { mode, errMessage } = this.state;
const { dirent, selectedDirentList, isMultipleOperation, path } = this.props;
const { mode, currentRepo, selectedRepo, selectedPath, showSearchBar, searchStatus, searchResults, selectedSearchedRepo } = this.state;
const copiedDirent = dirent || selectedDirentList[0];
const { permission } = copiedDirent;
const { isCustomPermission } = Utils.getUserPermission(permission);
const LibraryOption = ({ mode, label }) => (
<div className={`repo-list-item ${this.state.mode === mode ? 'active' : ''}`} onClick={() => this.onSelectedMode(mode)}>
<span className='library'>{label}</span>
</div>
);
return (
<Modal className='custom-modal' isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>
{isMultipleOperation ? this.renderTitle() : <div dangerouslySetInnerHTML={{ __html: this.renderTitle() }} className="d-flex mw-100"></div>}
<Modal className="custom-modal" isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}
close={
<div className="header-buttons">
<button type="button" className="close seahub-modal-btn" data-dismiss="modal" aria-label={gettext('Close')} onClick={this.toggle}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
{(isPro && !showSearchBar) &&
<button type="button" className="close seahub-modal-btn" data-dismiss="modal" aria-label={gettext('Search')} onClick={this.onOpenSearchBar}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-search" aria-hidden="true"></i>
</span>
</button>
}
</div>
}
>
{isMultipleOperation ? this.renderTitle() : <div dangerouslySetInnerHTML={{ __html: this.renderTitle() }} className="d-flex"></div>}
{(isPro && showSearchBar) &&
<Searcher
onUpdateMode={this.updateMode}
onUpdateSearchStatus={this.onUpdateSearchStatus}
onUpdateSearchResults={this.onUpdateSearchResults}
onClose={this.onCloseSearchBar}
/>
}
</ModalHeader>
<Row>
<Col className='repo-list-col border-right'>
<LibraryOption mode='only_current_library' label={gettext('Current Library')} />
{!isCustomPermission && <LibraryOption mode='only_other_libraries' label={gettext('Other Libraries')} />}
<LibraryOption mode='recently_used' label={gettext('Recently Used')} />
</Col>
<Col className='file-list-col'>
<ModalBody>
<FileChooser
repoID={repoID}
currentPath={path}
onDirentItemClick={this.onDirentItemClick}
onRepoItemClick={this.onRepoItemClick}
mode={mode}
hideLibraryName={false}
/>
{errMessage && <Alert color="danger" className="mt-2">{errMessage}</Alert>}
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
</ModalFooter>
</Col>
</Row>
<SelectDirentBody
mode={mode}
currentRepo={currentRepo}
selectedRepo={selectedRepo}
currentPath={path}
repoList={this.state.repoList}
selectedPath={selectedPath}
isSupportOtherLibraries={!isCustomPermission}
onCancel={this.toggle}
selectRepo={this.selectRepo}
setSelectedPath={this.setSelectedPath}
setErrMessage={this.setErrMessage}
handleSubmit={this.handleSubmit}
onUpdateMode={this.updateMode}
searchStatus={searchStatus}
searchResults={searchResults}
selectedSearchedItem={this.state.selectedSearchedItem}
onSelectedSearchedItem={this.selectSearchedItem}
onSearchedItemClick={this.onSearchedItemClick}
onSearchedItemDoubleClick={this.onSearchedItemDoubleClick}
selectedSearchedRepo={selectedSearchedRepo}
onSelectSearchedRepo={this.selectSearchedRepo}
onAddFolder={this.props.onAddFolder}
initToShowChildren={this.state.initToShowChildren}
fetchRepoInfo={this.fetchRepoInfo}
/>
</Modal>
);
}

View File

@ -1,7 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import { Modal, ModalBody } from 'reactstrap';
import { gettext } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
import '../../css/copy-move-dirent-progress-dialog.css';
const propTypes = {
@ -24,7 +26,7 @@ class CopyMoveDirentProgressDialog extends React.Component {
};
return (
<Modal isOpen={true} toggle={this.props.toggleDialog} className="copy-move-dirent-progress-dialog">
<ModalHeader toggle={this.props.toggleDialog}>{title}</ModalHeader>
<SeahubModalHeader toggle={this.props.toggleDialog}>{title}</SeahubModalHeader>
<ModalBody>
<div className="progress">
<div

View File

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { Button, Modal, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { gettext, maxFileName } from '../../utils/constants';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
onCreateRepo: PropTypes.func.isRequired,
@ -74,13 +75,14 @@ class CreateDepartmentRepoDialog extends React.Component {
render() {
return (
<Modal isOpen={true} toggle={this.toggle} autoFocus={false}>
<ModalHeader toggle={this.toggle}>{gettext('New Department Library')}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{gettext('New Department Library')}</SeahubModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="repo-name">{gettext('Name')}</Label>
<Input
id="repo-name"
name="repo-name"
onKeyDown={this.handleKeyDown}
value={this.state.repoName}
onChange={this.handleChange}

View File

@ -1,8 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { Button, Modal, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { Utils, validateName } from '../../utils/utils';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
fileType: PropTypes.string,
@ -88,13 +89,14 @@ class CreateFile extends React.Component {
const { toggleDialog } = this.props;
return (
<Modal isOpen={true} toggle={toggleDialog} onOpened={this.onAfterModelOpened}>
<ModalHeader toggle={toggleDialog}>{gettext('New File')}</ModalHeader>
<SeahubModalHeader toggle={toggleDialog}>{gettext('New File')}</SeahubModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="fileName">{gettext('Name')}</Label>
<Input
id="fileName"
name="file-name"
onKeyDown={this.handleKeyDown}
innerRef={this.newInput}
value={this.state.childName}

View File

@ -1,8 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { Button, Modal, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { Utils, validateName } from '../../utils/utils';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
fileType: PropTypes.string,
@ -76,13 +77,14 @@ class CreateForder extends React.Component {
render() {
return (
<Modal isOpen={true} toggle={this.toggle} autoFocus={false}>
<ModalHeader toggle={this.toggle}>{gettext('New Folder')}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{gettext('New Folder')}</SeahubModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="folderName">{gettext('Name')}</Label>
<Input
id="folderName"
name="folder-name"
value={this.state.childName}
onKeyDown={this.handleKeyDown}
onChange={this.handleChange}

View File

@ -2,8 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Modal, ModalHeader, ModalBody, ModalFooter, Input, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Label, Input, Button } from 'reactstrap';
import { Utils } from '../../utils/utils';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
class CreateGroupDialog extends React.Component {
@ -65,12 +66,13 @@ class CreateGroupDialog extends React.Component {
render() {
return (
<Modal isOpen={true} toggle={this.props.toggleDialog} autoFocus={false}>
<ModalHeader toggle={this.props.toggleDialog}>{gettext('New Group')}</ModalHeader>
<SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('New Group')}</SeahubModalHeader>
<ModalBody>
<label htmlFor="groupName">{gettext('Name')}</label>
<Label for="groupName">{gettext('Name')}</Label>
<Input
type="text"
id="groupName"
name="group-name"
value={this.state.groupName}
onChange={this.handleGroupChange}
onKeyDown={this.handleKeyDown}

View File

@ -1,8 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { Button, Modal, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
import { gettext, enableEncryptedLibrary, repoPasswordMinLength, storages, libraryTemplates } from '../../utils/constants';
import { SeahubSelect } from '../common/select';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
libraryType: PropTypes.string.isRequired,
@ -116,8 +117,7 @@ class CreateRepoDialog extends React.Component {
return true;
}
onPermissionChange = (e) => {
let permission = e.target.value;
onPermissionChange = (permission) => {
this.setState({ permission: permission });
};
@ -181,13 +181,14 @@ class CreateRepoDialog extends React.Component {
render() {
return (
<Modal isOpen={true} toggle={this.toggle} autoFocus={false}>
<ModalHeader toggle={this.toggle}>{gettext('New Library')}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{gettext('New Library')}</SeahubModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="repoName">{gettext('Name')}</Label>
<Input
id="repoName"
name="repo-name"
onKeyDown={this.handleKeyDown}
value={this.state.repoName}
onChange={this.handleRepoNameChange}
@ -221,11 +222,19 @@ class CreateRepoDialog extends React.Component {
{this.props.libraryType === 'group' && (
<FormGroup>
<Label for="exampleSelect">{gettext('Permission')}</Label>
<Input type="select" name="select" id="exampleSelect" onChange={this.onPermissionChange} value={this.state.permission}>
<option value='rw'>{gettext('Read-Write')}</option>
<option value='r'>{gettext('Read-Only')}</option>
</Input>
<Label>{gettext('Permission')}</Label>
<SeahubSelect
options={[
{ value: 'rw', label: gettext('Read-Write') },
{ value: 'r', label: gettext('Read-Only') }
]}
onChange={selectedOption => this.onPermissionChange(selectedOption.value)}
value={{
value: this.state.permission,
label: this.state.permission === 'rw' ? gettext('Read-Write') : gettext('Read-Only')
}}
isClearable={false}
/>
</FormGroup>
)}
{enableEncryptedLibrary &&

View File

@ -1,119 +0,0 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Button, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { TAG_COLORS } from '../../constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
const propTypes = {
repoID: PropTypes.string.isRequired,
onRepoTagCreated: PropTypes.func,
toggleCancel: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired
};
class CreateTagDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
tagName: '',
tagColor: TAG_COLORS[0],
newTag: {},
errorMsg: '',
};
}
inputNewName = (e) => {
this.setState({
tagName: e.target.value,
});
if (this.state.errorMsg) {
this.setState({ errorMsg: '' });
}
};
selectTagcolor = (e) => {
this.setState({
tagColor: e.target.value,
});
};
createTag = () => {
let name = this.state.tagName;
let color = this.state.tagColor;
let repoID = this.props.repoID;
seafileAPI.createRepoTag(repoID, name, color).then((res) => {
let repoTagID = res.data.repo_tag.repo_tag_id;
if (this.props.onRepoTagCreated) this.props.onRepoTagCreated(repoTagID);
this.props.toggleCancel();
}).catch((error) => {
let errMessage;
if (error.response.status === 500) {
errMessage = gettext('Internal Server Error');
} else if (error.response.status === 400) {
errMessage = gettext('Tag "{name}" already exists.');
errMessage = errMessage.replace('{name}', Utils.HTMLescape(name));
}
this.setState({ errorMsg: errMessage });
});
};
handleKeyDown = (e) => {
if (e.key === 'Enter') {
this.createTag();
}
};
render() {
let canSave = this.state.tagName.trim() ? true : false;
return (
<Fragment>
<ModalHeader toggle={this.props.onClose}>
<span className="tag-dialog-back sf3-font sf3-font-arrow rotate-180 d-inline-block" onClick={this.props.toggleCancel} aria-label={gettext('Back')}></span>
{gettext('New Tag')}
</ModalHeader>
<ModalBody>
<div role="form" className="tag-create">
<div className="form-group">
<label className="form-label">{gettext('Name')}</label>
<Input onKeyDown={this.handleKeyDown} autoFocus={true} value={this.state.tagName} onChange={this.inputNewName}/>
<div className="mt-2"><span className="error">{this.state.errorMsg}</span></div>
</div>
<div className="form-group">
<label className="form-label">{gettext('Select a color')}</label>
<div className="d-flex justify-content-between">
{TAG_COLORS.map((item, index) => {
return (
<div key={index} className="tag-color-option" onChange={this.selectTagcolor}>
<label className="colorinput">
{index === 0 ?
<input name="color" type="radio" value={item} className="colorinput-input" defaultChecked onClick={this.selectTagcolor}></input> :
<input name="color" type="radio" value={item} className="colorinput-input" onClick={this.selectTagcolor}></input>}
<span className="colorinput-color rounded-circle d-flex align-items-center justify-content-center" style={{ backgroundColor: item }}>
<i className="sf2-icon-tick color-selected"></i>
</span>
</label>
</div>
);
})
}
</div>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.props.toggleCancel}>{gettext('Cancel')}</Button>
{canSave ?
<Button color="primary" onClick={this.createTag}>{gettext('Save')}</Button> :
<Button color="primary" disabled>{gettext('Save')}</Button>
}
</ModalFooter>
</Fragment>
);
}
}
CreateTagDialog.propTypes = propTypes;
export default CreateTagDialog;

View File

@ -5,19 +5,7 @@ import { gettext } from '../../../utils/constants';
import Loading from '../../loading';
import OpIcon from '../../op-icon';
const propTypes = {
mode: PropTypes.string,
permission: PropTypes.object,
onChangeMode: PropTypes.func.isRequired,
onUpdateCustomPermission: PropTypes.func.isRequired,
};
class CustomPermissionEditor extends React.Component {
static defaultProps = {
mode: 'add'
};
constructor(props) {
super(props);
this.state = {
@ -50,7 +38,6 @@ class CustomPermissionEditor extends React.Component {
} else {
this.setState({ isLoading: false });
}
}
onChangePermissionName = (evt) => {
@ -108,12 +95,9 @@ class CustomPermissionEditor extends React.Component {
};
render() {
const { mode } = this.props;
const { mode = 'add' } = this.props;
const title = mode === 'add' ? gettext('Add permission') : gettext('Edit permission');
const { isLoading, permission_name, permission_desc, permission, errMessage } = this.state;
return (
<div className="custom-permission">
<div className="permission-header">
@ -212,6 +196,11 @@ class CustomPermissionEditor extends React.Component {
}
CustomPermissionEditor.propTypes = propTypes;
CustomPermissionEditor.propTypes = {
mode: PropTypes.string,
permission: PropTypes.object,
onChangeMode: PropTypes.func.isRequired,
onUpdateCustomPermission: PropTypes.func.isRequired,
};
export default CustomPermissionEditor;

View File

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { Button, Modal, ModalBody, ModalFooter } from 'reactstrap';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
currentNode: PropTypes.object.isRequired,
@ -24,7 +25,7 @@ class Delete extends React.Component {
}
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{title}</ModalHeader>
<SeahubModalHeader toggle={this.toggle}>{title}</SeahubModalHeader>
<ModalBody>
<p>{gettext('Are you sure you want to delete')}{' '}<b>{name}</b> ?</p>
</ModalBody>

View File

@ -1,9 +1,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { seafileAPI } from '../../utils/seafile-api';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
repoID: PropTypes.string.isRequired,
@ -53,7 +54,7 @@ class DeleteFolderDialog extends Component {
return (
<Modal isOpen={true} toggle={toggleDialog}>
<ModalHeader toggle={toggleDialog}>{gettext('Delete Folder')}</ModalHeader>
<SeahubModalHeader toggle={toggleDialog}>{gettext('Delete Folder')}</SeahubModalHeader>
<ModalBody>
<p dangerouslySetInnerHTML={{ __html: message }}></p>
{alert_message && <p className="error">{alert_message}</p>}

View File

@ -1,9 +1,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { seafileAPI } from '../../utils/seafile-api';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
repo: PropTypes.object.isRequired,
@ -30,12 +31,14 @@ class DeleteRepoDialog extends Component {
}
componentDidMount() {
seafileAPI.getRepoFolderShareInfo(this.props.repo.repo_id).then((res) => {
this.setState({
sharedToUserCount: res.data['shared_user_emails'].length,
sharedToGroupCount: res.data['shared_group_ids'].length,
if (this.props.isGetShare) {
seafileAPI.getRepoFolderShareInfo(this.props.repo.repo_id).then((res) => {
this.setState({
sharedToUserCount: res.data['shared_user_emails'].length,
sharedToGroupCount: res.data['shared_group_ids'].length,
});
});
});
}
}
onDeleteRepo = () => {
@ -63,7 +66,7 @@ class DeleteRepoDialog extends Component {
return (
<Modal isOpen={true} toggle={toggleDialog}>
<ModalHeader toggle={toggleDialog}>{gettext('Delete Library')}</ModalHeader>
<SeahubModalHeader toggle={toggleDialog}>{gettext('Delete Library')}</SeahubModalHeader>
<ModalBody>
<p dangerouslySetInnerHTML={{ __html: message }}></p>
{ alert_message != '' && <p className="error" dangerouslySetInnerHTML={{ __html: alert_message }}></p>}

View File

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { Button, Modal, ModalBody, ModalFooter } from 'reactstrap';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
toggleCancel: PropTypes.func.isRequired,
@ -14,7 +15,7 @@ const propTypes = {
function DeleteWikiDialog({ handleSubmit, toggleCancel, title, content, footer }) {
return (
<Modal isOpen={true} toggle={toggleCancel}>
<ModalHeader toggle={toggleCancel}>{title}</ModalHeader>
<SeahubModalHeader toggle={toggleCancel}>{title}</SeahubModalHeader>
<ModalBody>{content}</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={toggleCancel}>{gettext('Cancel')}</Button>

View File

@ -0,0 +1,249 @@
import React, { Fragment, } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalBody } from 'reactstrap';
import { gettext, isOrgContext, username } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api.js';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import EmptyTip from '../empty-tip';
import Loading from '../loading';
import Department from '../../models/department';
import SeahubModalHeader from '../common/seahub-modal-header';
import DepartmentGroup from './department-detail-widget/department-group';
import DepartmentGroupMembers from './department-detail-widget/department-group-members';
import DepartmentGroupMemberSelected from './department-detail-widget/department-group-member-selected';
import '../../css/manage-members-dialog.css';
import '../../css/group-departments.css';
const propTypes = {
groupID: PropTypes.any,
toggleManageMembersDialog: PropTypes.func,
toggleDepartmentDetailDialog: PropTypes.func,
isOwner: PropTypes.bool,
addUserShares: PropTypes.func,
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
userList: PropTypes.array,
};
class DepartmentDetailDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
departments: [],
departmentMembers: [],
newMembersTempObj: {},
currentDepartment: {},
departmentsLoading: true,
membersLoading: true,
selectedMemberMap: {},
departmentsTree: [],
};
}
componentDidMount() {
this.getSelectedMembers();
this.getDepartmentsList();
}
getSelectedMembers = () => {
const { usedFor, userList, groupID } = this.props;
if (usedFor === 'add_user_share') {
let selectedMemberMap = {};
selectedMemberMap[username] = true;
userList.forEach(member => {
selectedMemberMap[member.email] = true;
});
this.setState({ selectedMemberMap });
}
else if (usedFor === 'add_group_member') {
seafileAPI.listGroupMembers(groupID).then((res) => {
const groupMembers = res.data;
let selectedMemberMap = {};
selectedMemberMap[username] = true;
groupMembers.forEach(member => {
selectedMemberMap[member.email] = true;
});
this.setState({ selectedMemberMap });
}).catch(error => {
this.onError(error);
});
}
};
onError = (error) => {
let errMsg = Utils.getErrorMsg(error, true);
if (!error.response || error.response.status !== 403) {
toaster.danger(errMsg);
}
};
initDepartments(departments) {
const parentIdMap = {};
for (let i = 0; i < departments.length; i++) {
let item = departments[i];
parentIdMap[item.parent_group_id] = true;
}
return departments.map(depart => {
depart.hasChild = !!parentIdMap[depart.id];
depart.isExpanded = false;
return depart;
});
}
getDepartmentsList = () => {
seafileAPI.listAddressBookDepartments().then((res) => {
let departments = res.data.departments.map(item => {
return new Department(item);
});
let currentDepartment = departments.length > 0 ? departments[0] : {};
let departmentsTree = this.initDepartments(departments);
this.setState({
departments: departments,
currentDepartment: currentDepartment,
departmentsLoading: false,
departmentsTree: departmentsTree
});
this.getMembers(currentDepartment.id);
}).catch(error => {
this.onError(error);
});
};
getMembers = (department_id) => {
this.setState({ membersLoading: true });
seafileAPI.listAddressBookDepartmentMembers(department_id).then((res) => {
this.setState({
departmentMembers: res.data.members,
membersLoading: false,
});
}).catch(error => {
this.onError(error);
});
};
toggle = () => {
this.props.toggleDepartmentDetailDialog();
};
onMemberChecked = (member) => {
if (this.state.departmentMembers.indexOf(member) !== -1) {
let newMembersTempObj = this.state.newMembersTempObj;
if (member.email in newMembersTempObj) {
delete newMembersTempObj[member.email];
} else {
newMembersTempObj[member.email] = member;
}
this.setState({ newMembersTempObj: newMembersTempObj });
}
};
addGroupMember = () => {
let emails = Object.keys(this.state.newMembersTempObj);
seafileAPI.addGroupMembers(this.props.groupID, emails).then((res) => {
this.toggle();
this.props.toggleManageMembersDialog();
}).catch(error => {
this.onError(error);
});
};
addUserShares = () => {
this.props.addUserShares(this.state.newMembersTempObj);
};
removeSelectedMember = (email) => {
let newMembersTempObj = this.state.newMembersTempObj;
delete newMembersTempObj[email];
this.setState({ newMembersTempObj: newMembersTempObj });
};
setCurrent = (department) => {
this.setState({ currentDepartment: department });
};
selectAll = (members) => {
let { newMembersTempObj, selectedMemberMap } = this.state;
for (let member of members) {
if (Object.keys(selectedMemberMap).indexOf(member.email) !== -1) {
continue;
}
newMembersTempObj[member.email] = member;
}
this.setState({ newMembersTempObj: newMembersTempObj });
};
renderHeader = () => {
const title = this.props.usedFor === 'add_group_member' ? gettext('Select group members') : gettext('Select shared users');
return <SeahubModalHeader toggle={this.toggle}>{title}</SeahubModalHeader>;
};
render() {
let { departmentsLoading, departments } = this.state;
if (departmentsLoading) {
return (
<Modal isOpen={true} toggle={this.toggle}>
{this.renderHeader()}
<ModalBody>
<div className="d-flex flex-fill align-items-center"><Loading /></div>
</ModalBody>
</Modal>
);
}
const emptyTips = (
<Modal isOpen={true} toggle={this.toggle}>
{this.renderHeader()}
<ModalBody>
<EmptyTip>
<h2>{gettext('No departments')}</h2>
</EmptyTip>
</ModalBody>
</Modal>
);
const details = (
<Modal isOpen={true} toggle={this.toggle} className="department-dialog" style={{ maxWidth: '900px' }}>
{this.renderHeader()}
<ModalBody className="department-dialog-content">
<DepartmentGroup
departments={this.state.departments}
getMembers={this.getMembers}
setCurrent={this.setCurrent}
currentDepartment={this.state.currentDepartment}
loading={this.state.departmentsLoading}
departmentsTree={this.state.departmentsTree}
/>
<DepartmentGroupMembers
members={this.state.departmentMembers}
memberSelected={this.state.newMembersTempObj}
onUserChecked={this.onMemberChecked}
currentDepartment={this.state.currentDepartment}
selectAll={this.selectAll}
loading={this.state.membersLoading}
selectedMemberMap={this.state.selectedMemberMap}
isLoadingMore={this.state.isLoadingMore}
usedFor={this.props.usedFor}
/>
<DepartmentGroupMemberSelected
members={this.state.newMembersTempObj}
removeSelectedMember={this.removeSelectedMember}
addGroupMember={this.addGroupMember}
toggle={this.toggle}
addUserShares={this.addUserShares}
usedFor={this.props.usedFor}
/>
</ModalBody>
</Modal>
);
return (
<Fragment>
{(departments.length > 0 || isOrgContext) ? details : emptyTips}
</Fragment>
);
}
}
DepartmentDetailDialog.propTypes = propTypes;
export default DepartmentDetailDialog;

View File

@ -0,0 +1,107 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, ModalFooter } from 'reactstrap';
import { gettext } from '../../../utils/constants';
const ItemPropTypes = {
member: PropTypes.object,
removeSelectedMember: PropTypes.func
};
class Item extends Component {
constructor(props) {
super(props);
this.state = {
highlight: false,
};
}
handleMouseEnter = () => {
this.setState({ highlight: true });
};
handleMouseLeave = () => {
this.setState({ highlight: false });
};
removeSelectedMember = (email) => {
this.props.removeSelectedMember(email);
};
render() {
const { member } = this.props;
return (
<tr
className={this.state.highlight ? 'tr-highlight group-item' : 'group-item'}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
<td width="17%"><img className="avatar" src={member.avatar_url} alt=""/></td>
<td width="78%">{member.name}</td>
<td width="10%">
<i
className="sf3-font sf3-font-close cursor-pointer"
name={member.email}
onClick={this.removeSelectedMember.bind(this, member.email)}>
</i>
</td>
</tr>
);
}
}
Item.propTypes = ItemPropTypes;
const DepartmentGroupMemberSelectedPropTypes = {
members: PropTypes.object.isRequired,
removeSelectedMember: PropTypes.func.isRequired,
addGroupMember: PropTypes.func.isRequired,
toggle: PropTypes.func.isRequired,
usedFor: PropTypes.string,
addUserShares: PropTypes.func,
};
class DepartmentGroupMemberSelected extends Component {
render() {
const { members, usedFor } = this.props;
return (
<div className="department-dialog-member-selected pt-4">
<div style={{ height: 'calc(100% - 70px)' }}>
<div className='department-dialog-member-head px-4'>
<div className='department-name'>{gettext('Selected')}</div>
</div>
{Object.keys(members).length > 0 &&
<table className="department-dialog-member-table">
<tbody>
{Object.keys(members).map((email, index) => {
return (
<Item
key={index}
member={members[email]}
removeSelectedMember={this.props.removeSelectedMember}
/>
);
})}
</tbody>
</table>
}
</div>
<ModalFooter>
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
{usedFor === 'add_group_member' &&
<Button color="primary" onClick={this.props.addGroupMember}>{gettext('Add')}</Button>
}
{usedFor === 'add_user_share' &&
<Button color="primary" onClick={this.props.addUserShares}>{gettext('Add')}</Button>
}
</ModalFooter>
</div>
);
}
}
DepartmentGroupMemberSelected.propTypes = DepartmentGroupMemberSelectedPropTypes;
export default DepartmentGroupMemberSelected;

View File

@ -0,0 +1,170 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Tooltip } from 'reactstrap';
import { gettext, mediaUrl } from '../../../utils/constants';
import EmptyTip from '../../empty-tip';
import Loading from '../../loading';
const ItemPropTypes = {
member: PropTypes.object,
index: PropTypes.number,
tip: PropTypes.string,
memberSelected: PropTypes.object,
isMemberSelected: PropTypes.bool,
onUserChecked: PropTypes.func.isRequired,
};
class Item extends Component {
constructor(props) {
super(props);
this.state = {
highlight: false,
tooltipOpen: false,
};
}
handleMouseEnter = () => {
this.setState({ highlight: true });
};
handleMouseLeave = () => {
this.setState({ highlight: false });
};
onChange = (e) => {
const { member } = this.props;
this.props.onUserChecked(member);
};
toggleTooltip = () => {
this.setState({ tooltipOpen: !this.state.tooltipOpen });
};
render() {
const { member, memberSelected, isMemberSelected, index, tip } = this.props;
if (isMemberSelected) {
return (
<tr
className={this.state.highlight ? 'tr-highlight group-item' : 'group-item'}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
<td width="13%">
<input type="checkbox" className="vam" checked='checked' disabled/>
</td>
<td width="12%"><img className="avatar" src={member.avatar_url} alt=""/></td>
<td width="60%">{member.name}</td>
<td width="15%" className={this.state.highlight ? 'visible' : 'invisible' } id={`no-select-${index}`}>
<i className="sf3-font-help sf3-font"></i>
<Tooltip placement='bottom' isOpen={this.state.tooltipOpen} toggle={this.toggleTooltip} target={`no-select-${index}`} delay={{ show: 0, hide: 0 }} fade={false}>
{tip}
</Tooltip>
</td>
</tr>
);
}
return (
<tr
className={this.state.highlight ? 'tr-highlight group-item' : 'group-item'}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
<td width="13%">
<input
type="checkbox"
className="vam"
onChange={this.onChange}
checked={(member.email in memberSelected) ? 'checked' : ''}
/>
</td>
<td width="11%"><img className="avatar" src={member.avatar_url} alt=""/></td>
<td width="76%">{member.name}</td>
</tr>
);
}
}
Item.propTypes = ItemPropTypes;
const DepartmentGroupMembersPropTypes = {
members: PropTypes.array.isRequired,
memberSelected: PropTypes.object.isRequired,
onUserChecked: PropTypes.func.isRequired,
currentDepartment: PropTypes.object.isRequired,
selectedMemberMap: PropTypes.object,
selectAll: PropTypes.func.isRequired,
loading: PropTypes.bool,
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
};
class DepartmentGroupMembers extends Component {
selectAll = () => {
const { members } = this.props;
this.props.selectAll(members);
};
render() {
const { members, memberSelected, loading, selectedMemberMap, currentDepartment, usedFor } = this.props;
let headerTitle = (currentDepartment.name || '') + ' ' + gettext('members');
if (loading) {
return (
<div className="department-dialog-member pt-4">
<div className="w-100">
<div className='department-dialog-member-head px-4 mt-4'>
<Loading />
</div>
</div>
</div>
);
}
const enableSelectAll = Object.keys(memberSelected).length < members.length;
const tip = usedFor === 'add_group_member' ? gettext('User is already in this group') : gettext('It is already shared to user');
return (
<div className="department-dialog-member pt-4">
<div className="w-100">
<div className='department-dialog-member-head px-4'>
<div className='department-name'>
{headerTitle}
</div>
{enableSelectAll ?
<div className='select-all' onClick={this.selectAll}>{gettext('Select All')}</div>
:
<div className='select-all-disable'>{gettext('Select All')}</div>
}
</div>
{members.length > 0 ?
<Fragment>
<table className="department-dialog-member-table">
<tbody>
{members.map((member, index) => {
return (
<Item
key={index}
index={index}
member={member}
tip={tip}
memberSelected={memberSelected}
onUserChecked={this.props.onUserChecked}
isMemberSelected={selectedMemberMap[member.email]}
/>
);
})}
</tbody>
</table>
</Fragment>
:
<EmptyTip tipSrc={`${mediaUrl}img/no-users-tip.png`}>
<h2>{gettext('No members')}</h2>
</EmptyTip>
}
</div>
</div>
);
}
}
DepartmentGroupMembers.propTypes = DepartmentGroupMembersPropTypes;
export default DepartmentGroupMembers;

View File

@ -0,0 +1,143 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Loading from '../../loading';
import { isOrgContext } from '../../../utils/constants';
const ItemPropTypes = {
department: PropTypes.object,
departments: PropTypes.array,
getMembers: PropTypes.func.isRequired,
setCurrent: PropTypes.func.isRequired,
toggleExpanded: PropTypes.func.isRequired,
currentDepartment: PropTypes.object,
allMembersClick: PropTypes.bool,
};
class Item extends Component {
getMembers = (e) => {
e.stopPropagation();
const { department } = this.props;
this.props.getMembers(department.id);
this.props.setCurrent(department);
};
toggleExpanded = (e) => {
e.stopPropagation();
this.props.toggleExpanded(this.props.department.id, !this.props.department.isExpanded);
};
renderSubDepartments = () => {
const { departments } = this.props;
return (
<div>
{departments.map((department, index) => {
if (department.parent_group_id !== this.props.department.id) return null;
return (
<Item
key={department.id}
department={department}
departments={departments}
getMembers={this.props.getMembers}
setCurrent={this.props.setCurrent}
toggleExpanded={this.props.toggleExpanded}
currentDepartment={this.props.currentDepartment}
allMembersClick={this.props.allMembersClick}
padding={this.props.padding + 10}
/>
);
})}
</div>
);
};
render() {
const { department, currentDepartment, allMembersClick } = this.props;
const isCurrent = !allMembersClick && currentDepartment.id === department.id;
const { hasChild, isExpanded } = department;
return (
<>
<div className={isCurrent ? 'tr-highlight group-item' : 'group-item'} onClick={this.getMembers} style={{ paddingLeft: `${this.props.padding}px` }}>
{hasChild &&
<span
className={`sf3-font sf3-font-down ${isExpanded ? '' : 'rotate-270'} d-inline-block`}
onClick={this.toggleExpanded}
style={{ color: '#666' }}
>
</span>
}
<span style={hasChild ? { paddingLeft: '8px' } : { paddingLeft: '20px' }}>{department.name}</span>
</div>
{(isExpanded && hasChild) && this.renderSubDepartments()}
</>
);
}
}
Item.propTypes = ItemPropTypes;
const DepartmentGroupPropTypes = {
departments: PropTypes.array.isRequired,
getMembers: PropTypes.func.isRequired,
setCurrent: PropTypes.func.isRequired,
currentDepartment: PropTypes.object.isRequired,
loading: PropTypes.bool,
departmentsTree: PropTypes.array,
};
class DepartmentGroup extends Component {
constructor(props) {
super(props);
this.state = {
allMembersClick: !!isOrgContext
};
}
toggleExpanded = (id, state) => {
let departments = this.props.departmentsTree.slice(0);
let index = departments.findIndex(item => item.id === id);
departments[index].isExpanded = state;
this.setState({ departments });
};
getMembers = (department_id) => {
this.props.getMembers(department_id);
this.setState({ allMembersClick: false });
};
render() {
const { loading } = this.props;
let departments = this.props.departmentsTree;
if (loading) {
return (<Loading/>);
}
return (
<div className="department-dialog-group">
<div>
{departments.length > 0 && departments.map((department, index) => {
if (department.parent_group_id !== -1) return null;
return (
<Item
key={department.id}
department={department}
departments={departments}
getMembers={this.getMembers}
setCurrent={this.props.setCurrent}
toggleExpanded={this.toggleExpanded}
currentDepartment={this.props.currentDepartment}
allMembersClick={this.state.allMembersClick}
padding={10}
/>
);
})}
</div>
</div>
);
}
}
DepartmentGroup.propTypes = DepartmentGroupPropTypes;
export default DepartmentGroup;

View File

@ -1,21 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { Modal, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
class DismissGroupDialog extends React.Component {
constructor(props) {
super(props);
}
dismissGroup = () => {
let that = this;
seafileAPI.deleteGroup(this.props.groupID).then((res) => {
that.props.onGroupChanged();
const { groupID } = this.props;
seafileAPI.deleteGroup(groupID).then((res) => {
this.props.onGroupDeleted();
toaster.success(gettext('Group deleted'));
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
@ -24,13 +22,13 @@ class DismissGroupDialog extends React.Component {
render() {
return (
<Modal isOpen={this.props.showDismissGroupDialog} toggle={this.props.toggleDismissGroupDialog}>
<ModalHeader>{gettext('Delete Group')}</ModalHeader>
<Modal isOpen={true} toggle={this.props.toggleDialog}>
<SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('Delete Group')}</SeahubModalHeader>
<ModalBody>
<span>{gettext('Really want to delete this group?')}</span>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.props.toggleDismissGroupDialog}>{gettext('Cancel')}</Button>
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.dismissGroup}>{gettext('Delete')}</Button>
</ModalFooter>
</Modal>
@ -39,11 +37,9 @@ class DismissGroupDialog extends React.Component {
}
const DismissGroupDialogPropTypes = {
showDismissGroupDialog: PropTypes.bool.isRequired,
toggleDismissGroupDialog: PropTypes.func.isRequired,
loadGroup: PropTypes.func.isRequired,
groupID: PropTypes.string,
onGroupChanged: PropTypes.func.isRequired,
groupID: PropTypes.number.isRequired,
toggleDialog: PropTypes.func.isRequired,
onGroupDeleted: PropTypes.func.isRequired
};
DismissGroupDialog.propTypes = DismissGroupDialogPropTypes;

View File

@ -1,218 +0,0 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import CreateTagDialog from './create-tag-dialog';
import toaster from '../toast';
require('../../css/repo-tag.css');
const TagItemPropTypes = {
repoID: PropTypes.string.isRequired,
repoTag: PropTypes.object.isRequired,
filePath: PropTypes.string.isRequired,
fileTagList: PropTypes.array.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
};
class TagItem extends React.Component {
constructor(props) {
super(props);
this.state = {
isTagHighlighted: false
};
}
onMouseEnter = () => {
this.setState({
isTagHighlighted: true
});
};
onMouseLeave = () => {
this.setState({
isTagHighlighted: false
});
};
getRepoTagIdList = () => {
let repoTagIdList = [];
let fileTagList = this.props.fileTagList || [];
repoTagIdList = fileTagList.map((fileTag) => fileTag.repo_tag_id);
return repoTagIdList;
};
onEditFileTag = () => {
let { repoID, repoTag, filePath } = this.props;
let repoTagIdList = this.getRepoTagIdList();
if (repoTagIdList.indexOf(repoTag.id) === -1) {
let id = repoTag.id;
seafileAPI.addFileTag(repoID, filePath, id).then(() => {
repoTagIdList = this.getRepoTagIdList();
this.props.onFileTagChanged();
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
} else {
let fileTag = null;
let fileTagList = this.props.fileTagList;
for (let i = 0; i < fileTagList.length; i++) {
if (fileTagList[i].repo_tag_id === repoTag.id) {
fileTag = fileTagList[i];
break;
}
}
seafileAPI.deleteFileTag(repoID, fileTag.id).then(() => {
repoTagIdList = this.getRepoTagIdList();
this.props.onFileTagChanged();
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
};
render() {
const { isTagHighlighted } = this.state;
const { repoTag } = this.props;
const repoTagIdList = this.getRepoTagIdList();
const isTagSelected = repoTagIdList.indexOf(repoTag.id) != -1;
return (
<li
className={`tag-list-item cursor-pointer px-4 d-flex justify-content-between align-items-center ${isTagHighlighted ? 'hl' : ''}`}
onClick={this.onEditFileTag}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
>
<div className="d-flex align-items-center">
<span className="tag-color w-4 h-4 rounded-circle" style={{ backgroundColor: repoTag.color }}></span>
<span className="tag-name mx-2">{repoTag.name}</span>
</div>
{isTagSelected && <i className="sf2-icon-tick tag-selected-icon"></i>}
</li>
);
}
}
TagItem.propTypes = TagItemPropTypes;
const TagListPropTypes = {
repoID: PropTypes.string.isRequired,
repoTags: PropTypes.array.isRequired,
filePath: PropTypes.string.isRequired,
fileTagList: PropTypes.array.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
toggleCancel: PropTypes.func.isRequired,
createNewTag: PropTypes.func.isRequired,
};
class TagList extends React.Component {
render() {
const { repoTags } = this.props;
return (
<Fragment>
<ModalHeader toggle={this.props.toggleCancel}>{gettext('Select Tags')}</ModalHeader>
<ModalBody className="px-0">
<ul className="tag-list tag-list-container">
{repoTags.map((repoTag) => {
return (
<TagItem
key={repoTag.id}
repoTag={repoTag}
repoID={this.props.repoID}
filePath={this.props.filePath}
fileTagList={this.props.fileTagList}
onFileTagChanged={this.props.onFileTagChanged}
/>
);
})}
</ul>
<a
href="#"
className="add-tag-link px-4 py-2 d-flex align-items-center"
onClick={this.props.createNewTag}
>
<span className="sf2-icon-plus mr-2"></span>
{gettext('Create a new tag')}
</a>
</ModalBody>
<ModalFooter>
<Button onClick={this.props.toggleCancel}>{gettext('Close')}</Button>
</ModalFooter>
</Fragment>
);
}
}
TagList.propTypes = TagListPropTypes;
const propTypes = {
repoID: PropTypes.string.isRequired,
repoTags: PropTypes.array.isRequired,
filePath: PropTypes.string.isRequired,
fileTagList: PropTypes.array.isRequired,
toggleCancel: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
};
class EditFileTagDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
isCreateRepoTagShow: false,
isListRepoTagShow: true,
};
}
createNewTag = () => {
this.setState({
isCreateRepoTagShow: !this.state.isCreateRepoTagShow,
isListRepoTagShow: !this.state.isListRepoTagShow,
});
};
onRepoTagCreated = (repoTagID) => {
let { repoID, filePath } = this.props;
seafileAPI.addFileTag(repoID, filePath, repoTagID).then(() => {
this.props.onFileTagChanged();
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
};
render() {
return (
<Modal isOpen={true} toggle={this.props.toggleCancel} autoFocus={false}>
{this.state.isListRepoTagShow &&
<TagList
repoID={this.props.repoID}
repoTags={this.props.repoTags}
filePath={this.props.filePath}
fileTagList={this.props.fileTagList}
onFileTagChanged={this.props.onFileTagChanged}
toggleCancel={this.props.toggleCancel}
createNewTag={this.createNewTag}
/>
}
{this.state.isCreateRepoTagShow &&
<CreateTagDialog
repoID={this.props.repoID}
onClose={this.props.toggleCancel}
toggleCancel={this.createNewTag}
onRepoTagCreated={this.onRepoTagCreated}
/>
}
</Modal>
);
}
}
EditFileTagDialog.propTypes = propTypes;
export default EditFileTagDialog;

View File

@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { Modal, ModalBody, ModalFooter } from 'reactstrap';
import CreatableSelect from 'react-select/creatable';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
const propTypes = {
repoID: PropTypes.string.isRequired,
@ -57,8 +58,8 @@ class UpdateRepoCommitLabels extends React.Component {
render() {
const { formErrorMsg } = this.state;
return (
<Modal isOpen={true} centered={true} toggle={this.props.toggleDialog}>
<ModalHeader toggle={this.props.toggleDialog}>{gettext('Edit labels')}</ModalHeader>
<Modal isOpen={true} toggle={this.props.toggleDialog}>
<SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('Edit labels')}</SeahubModalHeader>
<ModalBody>
<React.Fragment>
<CreatableSelect

View File

@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import { Modal, ModalBody } from 'reactstrap';
import { Utils } from '../../utils/utils';
import { gettext, siteRoot } from '../../utils/constants';
import { fileAccessLogAPI } from '../../utils/file-access-log-api';
import toaster from '../toast';
import Loading from '../loading';
import EmptyTip from '../empty-tip';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
import '../../css/file-access-log.css';
@ -88,9 +89,9 @@ class FileAccessLog extends React.Component {
return (
<Modal isOpen={true} toggle={this.props.toggleDialog} className="file-access-log-container">
<ModalHeader toggle={this.props.toggleDialog}>
<SeahubModalHeader toggle={this.props.toggleDialog}>
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
</ModalHeader>
</SeahubModalHeader>
<ModalBody className="file-access-log-content-container" onScroll={this.handleScroll}>
{isLoading ? <Loading /> : (
<>

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import copy from 'copy-to-clipboard';
import dayjs from 'dayjs';
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
import { Button, Form, FormGroup, Label, Input, InputGroup, Alert } from 'reactstrap';
import { gettext, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, canSendShareLinkEmail, uploadLinkExpireDaysMin, uploadLinkExpireDaysMax, uploadLinkExpireDaysDefault } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
@ -300,14 +300,12 @@ class GenerateUploadLink extends React.Component {
<Input type="text" readOnly={true} value={sharedUploadInfo.password} /> :
<Input type="text" readOnly={true} value={'***************'} />
}
<InputGroupAddon addonType="append">
<Button
aria-label={this.state.storedPasswordVisible ? gettext('Hide') : gettext('Show')}
onClick={this.toggleStoredPasswordVisible}
className={`link-operation-icon eye-icon sf3-font sf3-font-eye${this.state.storedPasswordVisible ? '' : '-slash'}`}
>
</Button>
</InputGroupAddon>
<Button
aria-label={this.state.storedPasswordVisible ? gettext('Hide') : gettext('Show')}
onClick={this.toggleStoredPasswordVisible}
className={`link-operation-icon eye-icon sf3-font sf3-font-eye${this.state.storedPasswordVisible ? '' : '-slash'}`}
>
</Button>
</InputGroup>
</dd>
</>
@ -338,15 +336,13 @@ class GenerateUploadLink extends React.Component {
) : (
<InputGroup className="share-link-details-item">
<Input type="text" readOnly={true} value={dayjs(sharedUploadInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')} />
<InputGroupAddon addonType="append">
<Button
aria-label={gettext('Edit')}
title={gettext('Edit')}
className="link-operation-icon sf3-font sf3-font-rename"
onClick={this.editExpirationToggle}
>
</Button>
</InputGroupAddon>
<Button
aria-label={gettext('Edit')}
title={gettext('Edit')}
className="link-operation-icon sf3-font sf3-font-rename"
onClick={this.editExpirationToggle}
>
</Button>
</InputGroup>
)}
</dd>
@ -389,14 +385,12 @@ class GenerateUploadLink extends React.Component {
<span className="tip">{passwordLengthTip}</span>
<InputGroup style={{ width: inputWidth }}>
<Input id="passwd" type={this.state.passwordVisible ? 'text' : 'password'} value={this.state.password || ''} onChange={this.inputPassword} />
<InputGroupAddon addonType="append">
<Button onClick={this.togglePasswordVisible}>
<i className={`link-operation-icon sf3-font sf3-font-eye${this.state.passwordVisible ? '' : '-slash'}`}></i>
</Button>
<Button onClick={this.generatePassword}>
<i className="link-operation-icon sf3-font sf3-font-magic"></i>
</Button>
</InputGroupAddon>
<Button onClick={this.togglePasswordVisible}>
<i className={`link-operation-icon sf3-font sf3-font-eye${this.state.passwordVisible ? '' : '-slash'}`}></i>
</Button>
<Button onClick={this.generatePassword}>
<i className="link-operation-icon sf3-font sf3-font-magic"></i>
</Button>
</InputGroup>
</FormGroup>
<FormGroup>

View File

@ -0,0 +1,111 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalBody, Label } from 'reactstrap';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
import copy from 'copy-to-clipboard';
import toaster from '../toast';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import '../../css/group-invite-members-dialog.css';
const propTypes = {
groupID: PropTypes.number.isRequired,
toggleInviteMembersDialog: PropTypes.func.isRequired,
};
class GroupInviteMembersDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
inviteList: [],
};
}
componentDidMount() {
this.listInviteLinks();
}
listInviteLinks = () => {
seafileAPI.getGroupInviteLinks(this.props.groupID).then((res) => {
this.setState({ inviteList: res.data.group_invite_link_list });
}).catch(error => {
this.onError(error);
});
};
addInviteLink = () => {
seafileAPI.addGroupInviteLinks(this.props.groupID).then(() => {
this.listInviteLinks();
}).catch(error => {
this.onError(error);
});
};
deleteLink = (token) => {
seafileAPI.deleteGroupInviteLinks(this.props.groupID, token).then(() => {
this.listInviteLinks();
}).catch(error => {
this.onError(error);
});
};
onError = (error) => {
let errMsg = Utils.getErrorMsg(error, true);
if (!error.response || error.response.status !== 403) {
toaster.danger(errMsg);
}
};
copyLink = () => {
const inviteLinkItem = this.state.inviteList[0];
copy(inviteLinkItem.link);
const message = gettext('Invitation link has been copied to clipboard');
toaster.success((message), {
duration: 2
});
};
toggle = () => {
this.props.toggleInviteMembersDialog();
};
render() {
const { inviteList } = this.state;
const link = inviteList[0];
return (
<Modal isOpen={true} toggle={this.toggle} className="group-invite-members">
<SeahubModalHeader toggle={this.toggle}>{gettext('Invite members')}</SeahubModalHeader>
<ModalBody>
{link ?
<>
<Label>{gettext('Group invitation link')}</Label>
<div className="invite-link-item">
<div className="form-item text-truncate">{link.link}</div>
<div className="invite-link-copy">
<Button color="primary" onClick={this.copyLink} className="invite-link-copy-btn text-truncate">{gettext('Copy')}</Button>
</div>
<Button color="primary" outline onClick={this.deleteLink.bind(this, link.token)} className="delete-link-btn ml-2">
<i className="sf3-font-delete1 sf3-font"></i>
</Button>
</div>
</>
:
<>
<div className="no-link-tip mb-4">
{gettext('No group invitation link yet. Group invitation link let registered users to join the group by clicking a link.')}
</div>
<Button color="primary" onClick={this.addInviteLink} className="my-4">{gettext('Generate')}</Button>
</>
}
</ModalBody>
</Modal>
);
}
}
GroupInviteMembersDialog.propTypes = propTypes;
export default GroupInviteMembersDialog;

View File

@ -1,14 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import { Modal, ModalBody } from 'reactstrap';
import { Utils } from '../../utils/utils';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import toaster from '../toast';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
import Loading from '../loading';
const propTypes = {
groupID: PropTypes.string.isRequired,
groupID: PropTypes.number.isRequired,
toggleDialog: PropTypes.func.isRequired
};
@ -82,7 +83,7 @@ class GroupMembers extends React.Component {
}
return (
<Modal isOpen={true} toggle={this.props.toggleDialog}>
<ModalHeader toggle={this.props.toggleDialog}>{`${gettext('Group members')} (${memberNumber})`}</ModalHeader>
<SeahubModalHeader toggle={this.props.toggleDialog}>{`${gettext('Group members')} (${memberNumber})`}</SeahubModalHeader>
<ModalBody className="px-0 group-members-container" onScroll={this.handleScroll}>
{isLoading ? <Loading /> : (
<>

View File

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalBody } from 'reactstrap';
import { gettext, mediaUrl, siteName, canAddRepo } from '../../utils/constants';
import '../../css/seahub-modal-header.css';
const propTypes = {
toggleDialog: PropTypes.func.isRequired
@ -17,7 +18,11 @@ class GuideForNewDialog extends React.Component {
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalBody>
<button type="button" className="close text-gray" onClick={this.toggle}><span aria-hidden="true">×</span></button>
<button type="button" className="close seahub-modal-btn p-0" aria-label={gettext('Close')} onClick={this.toggle}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
<div className="p-2 text-center">
<img src={`${mediaUrl}img/welcome.png`} width="408" alt="" />
<h3 id="dialogTitle" className="mt-6 mb-4">{gettext('Welcome to {site_name_placeholder}').replace('{site_name_placeholder}', siteName)}</h3>

View File

@ -1,76 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import Lightbox from '@seafile/react-image-lightbox';
import '@seafile/react-image-lightbox/style.css';
const propTypes = {
imageItems: PropTypes.array.isRequired,
imageIndex: PropTypes.number.isRequired,
closeImagePopup: PropTypes.func.isRequired,
moveToPrevImage: PropTypes.func.isRequired,
moveToNextImage: PropTypes.func.isRequired,
onDeleteImage: PropTypes.func,
onRotateImage: PropTypes.func,
enableRotate: PropTypes.bool,
};
class ImageDialog extends React.Component {
downloadImage = (url) => {
location.href = url;
};
onViewOriginal = () => {
window.open(this.props.imageItems[this.props.imageIndex].url, '_blank');
};
render() {
const { imageItems, imageIndex, closeImagePopup, moveToPrevImage, moveToNextImage, onDeleteImage, onRotateImage } = this.props;
const imageItemsLength = imageItems.length;
if (imageItemsLength === 0) return null;
const name = imageItems[imageIndex].name;
const mainImg = imageItems[imageIndex];
const nextImg = imageItems[(imageIndex + 1) % imageItemsLength];
const prevImg = imageItems[(imageIndex + imageItemsLength - 1) % imageItemsLength];
// The backend server does not support rotating HEIC images
let enableRotate = this.props.enableRotate;
const suffix = mainImg.src.slice(mainImg.src.lastIndexOf('.') + 1, mainImg.src.lastIndexOf('?')).toLowerCase();
if (suffix === 'heic') {
enableRotate = false;
}
return (
<Lightbox
wrapperClassName='custom-image-previewer'
imageTitle={`${name} (${imageIndex + 1}/${imageItemsLength})`}
mainSrc={mainImg.thumbnail || mainImg.src}
nextSrc={nextImg.thumbnail || nextImg.src}
prevSrc={prevImg.thumbnail || prevImg.src}
onCloseRequest={closeImagePopup}
onMovePrevRequest={moveToPrevImage}
onMoveNextRequest={moveToNextImage}
imagePadding={70}
imageLoadErrorMessage={gettext('The image could not be loaded.')}
prevLabel={gettext('Previous (Left arrow key)')}
nextLabel={gettext('Next (Right arrow key)')}
closeLabel={gettext('Close (Esc)')}
zoomInLabel={gettext('Zoom in')}
zoomOutLabel={gettext('Zoom out')}
enableRotate={enableRotate}
onClickDownload={() => this.downloadImage(imageItems[imageIndex].downloadURL)}
onClickDelete={onDeleteImage ? () => onDeleteImage(name) : null}
onViewOriginal={this.onViewOriginal}
viewOriginalImageLabel={gettext('View original image')}
onRotateImage={(onRotateImage && enableRotate) ? (angle) => onRotateImage(imageIndex, angle) : null}
/>
);
}
}
ImageDialog.propTypes = propTypes;
ImageDialog.defaultProps = {
enableRotate: true,
};
export default ImageDialog;

Some files were not shown because too many files have changed in this diff Show More