├── .git-blame-ignore-revs ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── general_query.md ├── file-preview-modified.png ├── file_preview.png ├── grid.png ├── grid_context.png ├── home-modified.png ├── list.png ├── logo.svg ├── logo_dark.svg ├── logo_light.svg ├── new_logo.svg ├── og_1200.png ├── search-modified.png ├── share-modified.png ├── share_menu.png ├── text_editor.png ├── text_editor_realtime.png ├── try-on-f-cloud-button.svg ├── try_on_FC_dark.svg ├── try_on_FC_light.svg ├── upload-modified.png └── workflows │ └── build-image.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── MANIFEST.in ├── README.md ├── docker ├── docker-compose.yml └── init.sh ├── drive ├── __init__.py ├── api │ ├── __init__.py │ ├── activity.py │ ├── embed.py │ ├── files.py │ ├── list.py │ ├── notifications.py │ ├── permissions.py │ ├── product.py │ ├── storage.py │ └── tags.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── drive │ ├── __init__.py │ └── doctype │ │ ├── __init__.py │ │ ├── account_request │ │ ├── __init__.py │ │ ├── account_request.js │ │ ├── account_request.json │ │ ├── account_request.py │ │ └── test_account_request.py │ │ ├── drive_document │ │ ├── __init__.py │ │ ├── drive_document.js │ │ ├── drive_document.json │ │ ├── drive_document.py │ │ └── test_drive_document.py │ │ ├── drive_document_version │ │ ├── __init__.py │ │ ├── drive_document_version.js │ │ ├── drive_document_version.json │ │ ├── drive_document_version.py │ │ └── test_drive_document_version.py │ │ ├── drive_entity_activity_log │ │ ├── __init__.py │ │ ├── drive_entity_activity_log.js │ │ ├── drive_entity_activity_log.json │ │ ├── drive_entity_activity_log.py │ │ ├── patches │ │ │ ├── initialize_creation.py │ │ │ └── share_creation.py │ │ └── test_drive_entity_activity_log.py │ │ ├── drive_entity_log │ │ ├── __init__.py │ │ ├── drive_entity_log.js │ │ ├── drive_entity_log.json │ │ ├── drive_entity_log.py │ │ └── test_drive_entity_log.py │ │ ├── drive_entity_tag │ │ ├── __init__.py │ │ ├── drive_entity_tag.json │ │ └── drive_entity_tag.py │ │ ├── drive_favourite │ │ ├── __init__.py │ │ ├── drive_favourite.js │ │ ├── drive_favourite.json │ │ ├── drive_favourite.py │ │ └── test_drive_favourite.py │ │ ├── drive_file │ │ ├── __init__.py │ │ ├── drive_file.js │ │ ├── drive_file.json │ │ ├── drive_file.py │ │ └── test_drive_file.py │ │ ├── drive_notification │ │ ├── __init__.py │ │ ├── drive_notification.js │ │ ├── drive_notification.json │ │ ├── drive_notification.py │ │ └── test_drive_notification.py │ │ ├── drive_permission │ │ ├── __init__.py │ │ ├── drive_permission.js │ │ ├── drive_permission.json │ │ ├── drive_permission.py │ │ └── test_drive_permission.py │ │ ├── drive_s3_settings │ │ ├── __init__.py │ │ ├── drive_s3_settings.js │ │ ├── drive_s3_settings.json │ │ ├── drive_s3_settings.py │ │ └── test_drive_s3_settings.py │ │ ├── drive_settings │ │ ├── __init__.py │ │ ├── drive_settings.js │ │ ├── drive_settings.json │ │ ├── drive_settings.py │ │ └── test_drive_settings.py │ │ ├── drive_site_settings │ │ ├── __init__.py │ │ ├── drive_site_settings.js │ │ ├── drive_site_settings.json │ │ ├── drive_site_settings.py │ │ └── test_drive_site_settings.py │ │ ├── drive_tag │ │ ├── __init__.py │ │ ├── drive_tag.js │ │ ├── drive_tag.json │ │ ├── drive_tag.py │ │ └── test_drive_tag.py │ │ ├── drive_team │ │ ├── __init__.py │ │ ├── drive_team.js │ │ ├── drive_team.json │ │ ├── drive_team.py │ │ └── test_drive_team.py │ │ ├── drive_team_member │ │ ├── __init__.py │ │ ├── drive_team_member.json │ │ └── drive_team_member.py │ │ └── drive_user_invitation │ │ ├── __init__.py │ │ ├── drive_user_invitation.js │ │ ├── drive_user_invitation.json │ │ ├── drive_user_invitation.py │ │ └── test_drive_user_invitation.py ├── hooks.py ├── install.py ├── locks │ └── distributed_lock.py ├── modules.txt ├── patches.txt ├── patches │ ├── __init__.py │ ├── folder_size.py │ ├── settings.py │ └── team_restructure.py ├── public │ └── .gitkeep ├── templates │ ├── __init__.py │ └── emails │ │ ├── drive_invitation.html │ │ ├── drive_share.html │ │ └── otp.html ├── utils │ ├── __init__.py │ ├── dev.py │ ├── files.py │ └── users.py └── www │ ├── __init__.py │ └── drive.py ├── frontend ├── .eslintrc.js ├── .husky │ └── pre-commit ├── .prettierrc ├── components.d.ts ├── index.html ├── jsconfig.json ├── package.json ├── postcss.config.js ├── public │ ├── browserconfig.xml │ ├── color-circle.png │ ├── favicon-114x114.png │ ├── favicon-120x120.png │ ├── favicon-128x128.png │ ├── favicon-144x144.png │ ├── favicon-150x150.png │ ├── favicon-152x152.png │ ├── favicon-16x16.png │ ├── favicon-180x180.png │ ├── favicon-192x192.png │ ├── favicon-310x310.png │ ├── favicon-32x32.png │ ├── favicon-384x384.png │ ├── favicon-512x512.png │ ├── favicon-57x57.png │ ├── favicon-60x60.png │ ├── favicon-70x70.png │ ├── favicon-72x72.png │ ├── favicon-76x76.png │ ├── favicon-96x96.png │ ├── favicon.ico │ └── manifest.json ├── src │ ├── App.vue │ ├── assets │ │ └── images │ │ │ └── icons │ │ │ ├── Link.svg │ │ │ ├── after effects.svg │ │ │ ├── application.svg │ │ │ ├── archive.svg │ │ │ ├── audio.svg │ │ │ ├── code.svg │ │ │ ├── document.svg │ │ │ ├── excel.svg │ │ │ ├── folder.svg │ │ │ ├── image.svg │ │ │ ├── logo.svg │ │ │ ├── markdown.svg │ │ │ ├── page-break.svg │ │ │ ├── pdf.svg │ │ │ ├── photoshop.svg │ │ │ ├── presentation.svg │ │ │ ├── shared-folder.svg │ │ │ ├── sketch.svg │ │ │ ├── spreadsheet.svg │ │ │ ├── text.svg │ │ │ ├── unknown.svg │ │ │ ├── video.svg │ │ │ └── word.svg │ ├── components │ │ ├── ActivityTree.vue │ │ ├── ActivityTreeItem.vue │ │ ├── ActivityTreeShare.vue │ │ ├── AppSwitcher.vue │ │ ├── BottomBar.vue │ │ ├── CTADeleteDialog.vue │ │ ├── ColorPopover.vue │ │ ├── ContextMenu.vue │ │ ├── CustomListRow.vue │ │ ├── CustomListRowItem.vue │ │ ├── DeleteDialog.vue │ │ ├── Dialogs.vue │ │ ├── DocEditor │ │ │ ├── PreviewEditor.vue │ │ │ ├── TextEditor.vue │ │ │ ├── commands.js │ │ │ ├── components │ │ │ │ ├── AnnotationList.vue │ │ │ │ ├── ColorInput.vue │ │ │ │ ├── ColorPicker.vue │ │ │ │ ├── DocMenuAndInfoBar.vue │ │ │ │ ├── FontColor.vue │ │ │ │ ├── InsertImage.vue │ │ │ │ ├── InsertLink.vue │ │ │ │ ├── InsertVideo.vue │ │ │ │ ├── MentionList.vue │ │ │ │ ├── Menu.vue │ │ │ │ ├── MenuBar.vue │ │ │ │ ├── NewAnnotation.vue │ │ │ │ ├── NewComment.vue │ │ │ │ ├── NewManualSnapshotDialog.vue │ │ │ │ ├── OuterComment.vue │ │ │ │ ├── ResizableMediaNodeView.vue │ │ │ │ ├── SnapshotPreviewDialog.vue │ │ │ │ ├── TableBubbleMenu.vue │ │ │ │ ├── TableCellMenu.vue │ │ │ │ ├── TableColumnMenu.vue │ │ │ │ ├── TableRowMenu.vue │ │ │ │ └── suggestionList.vue │ │ │ ├── editor.css │ │ │ ├── extensions │ │ │ │ ├── AnnotationExtension │ │ │ │ │ └── annotation.ts │ │ │ │ ├── DetailsExtension │ │ │ │ │ └── DetailsExtension │ │ │ │ │ │ ├── details-item.ts │ │ │ │ │ │ ├── details.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── summary.ts │ │ │ │ ├── InsDelMark.ts │ │ │ │ ├── InsDelNode.ts │ │ │ │ ├── Pagebreak.ts │ │ │ │ ├── backgroundColor.ts │ │ │ │ ├── character-count.ts │ │ │ │ ├── collaboration.ts │ │ │ │ ├── collaborationCursor.ts │ │ │ │ ├── color.ts │ │ │ │ ├── comment.ts │ │ │ │ ├── createDiffMark.ts │ │ │ │ ├── diffType.js │ │ │ │ ├── document.ts │ │ │ │ ├── font-family.ts │ │ │ │ ├── font-size.ts │ │ │ │ ├── image-extension.js │ │ │ │ ├── indent.ts │ │ │ │ ├── lineHeight.ts │ │ │ │ ├── link.ts │ │ │ │ ├── mention │ │ │ │ │ ├── MentionExtension.ts │ │ │ │ │ └── mention.js │ │ │ │ ├── paragraph.ts │ │ │ │ ├── placeholder.ts │ │ │ │ ├── resizenode │ │ │ │ │ ├── dropMedia.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── resizableMedia.ts │ │ │ │ │ └── resizableMediaMenuUtil.ts │ │ │ │ ├── suggestion │ │ │ │ │ ├── suggestion.js │ │ │ │ │ └── suggestionExtension.js │ │ │ │ ├── table │ │ │ │ │ ├── cell.ts │ │ │ │ │ ├── header.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── row.ts │ │ │ │ │ ├── table.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── text-align.ts │ │ │ │ ├── text-style.ts │ │ │ │ ├── text.ts │ │ │ │ ├── typography.ts │ │ │ │ ├── underline.ts │ │ │ │ └── video-extension.js │ │ │ ├── genDiff.js │ │ │ ├── icons │ │ │ │ ├── AlignCenter.vue │ │ │ │ ├── AlignJustify.vue │ │ │ │ ├── AlignLeft.vue │ │ │ │ ├── AlignRight.vue │ │ │ │ ├── BlockQuote.vue │ │ │ │ ├── Bold.vue │ │ │ │ ├── Check.vue │ │ │ │ ├── Code.vue │ │ │ │ ├── Codeblock.vue │ │ │ │ ├── Details.vue │ │ │ │ ├── Image.vue │ │ │ │ ├── Indent.vue │ │ │ │ ├── Italic.vue │ │ │ │ ├── List.vue │ │ │ │ ├── Mention.vue │ │ │ │ ├── NewAnnotation.vue │ │ │ │ ├── NewLink.vue │ │ │ │ ├── OrderList.vue │ │ │ │ ├── Outdent.vue │ │ │ │ ├── PageBreak.vue │ │ │ │ ├── StrikeThrough.vue │ │ │ │ ├── Style.vue │ │ │ │ ├── ToggleHeaderCell.vue │ │ │ │ ├── Underline.vue │ │ │ │ ├── Video.vue │ │ │ │ ├── align-center.vue │ │ │ │ ├── align-item-center.vue │ │ │ │ ├── align-item-left.vue │ │ │ │ ├── align-item-right.vue │ │ │ │ ├── align-left.vue │ │ │ │ ├── align-right.vue │ │ │ │ ├── delete.vue │ │ │ │ ├── float-item-left.vue │ │ │ │ ├── float-item-right.vue │ │ │ │ ├── float-left.vue │ │ │ │ ├── float-right.vue │ │ │ │ ├── line-height.vue │ │ │ │ └── quote.vue │ │ │ ├── index.js │ │ │ ├── utils.js │ │ │ └── utils │ │ │ │ ├── deleteNodes.ts │ │ │ │ ├── getNestedNodes.ts │ │ │ │ └── getSelectedContent.ts │ │ ├── DriveToolBar.vue │ │ ├── EntityToolbar.vue │ │ ├── EspressoIcons │ │ │ ├── AddUser.vue │ │ │ ├── Apps.vue │ │ │ ├── Check.vue │ │ │ ├── ChevronDown.vue │ │ │ ├── Clock.vue │ │ │ ├── Cloud.vue │ │ │ ├── Colour-picker.vue │ │ │ ├── Comment.vue │ │ │ ├── Diamond.vue │ │ │ ├── Docs.vue │ │ │ ├── DownArrow.vue │ │ │ ├── Download.vue │ │ │ ├── Edit.vue │ │ │ ├── File-upload.vue │ │ │ ├── File.vue │ │ │ ├── Filter.vue │ │ │ ├── Folder-upload.vue │ │ │ ├── Folder.vue │ │ │ ├── Globe.vue │ │ │ ├── Groups.vue │ │ │ ├── Home.vue │ │ │ ├── Info.vue │ │ │ ├── Link.vue │ │ │ ├── Lock.vue │ │ │ ├── Move-2.vue │ │ │ ├── Move.vue │ │ │ ├── MyDrive.vue │ │ │ ├── NewFile.vue │ │ │ ├── NewFolder.vue │ │ │ ├── Open.vue │ │ │ ├── Organization.vue │ │ │ ├── Palette.vue │ │ │ ├── Preview.vue │ │ │ ├── Printer.vue │ │ │ ├── Recent.vue │ │ │ ├── Rename.vue │ │ │ ├── Search.vue │ │ │ ├── Share.vue │ │ │ ├── ShareNew.vue │ │ │ ├── Sort.vue │ │ │ ├── Star.vue │ │ │ ├── Trash.vue │ │ │ ├── User.vue │ │ │ ├── Users.vue │ │ │ ├── View.vue │ │ │ ├── ViewGrid.vue │ │ │ └── ViewList.vue │ │ ├── FilePicker.vue │ │ ├── FilePreview.vue │ │ ├── FileRender.vue │ │ ├── FileTypePreview │ │ │ ├── AudioPreview.vue │ │ │ ├── DocPreview.vue │ │ │ ├── ImagePreview.vue │ │ │ ├── MSOfficePreview.vue │ │ │ ├── PDFPreview.vue │ │ │ ├── SheetPreview.vue │ │ │ ├── TextPreview.vue │ │ │ └── VideoPreview.vue │ │ ├── FileUploader.vue │ │ ├── FolderContentsError.vue │ │ ├── FrappeDriveLogo.vue │ │ ├── FrappeFileLine.vue │ │ ├── FrappeFolder.vue │ │ ├── FrappeFolderLine.vue │ │ ├── FrappeLogo.vue │ │ ├── GeneralAccess.vue │ │ ├── GeneralDialog.vue │ │ ├── GenericPage.vue │ │ ├── GridItem.vue │ │ ├── GridView.vue │ │ ├── InfoPopup.vue │ │ ├── InfoSidebar.vue │ │ ├── ListView.vue │ │ ├── Loader.vue │ │ ├── MimeIcons │ │ │ ├── Archive.vue │ │ │ ├── Audio.vue │ │ │ ├── Document.vue │ │ │ ├── Folder.vue │ │ │ ├── Image.vue │ │ │ ├── PDF.vue │ │ │ ├── Presentation.vue │ │ │ ├── Spreadsheet.vue │ │ │ ├── Unknown.vue │ │ │ └── Video.vue │ │ ├── MobileSidebar.vue │ │ ├── MoveDialog.vue │ │ ├── Navbar.vue │ │ ├── NewFolderDialog.vue │ │ ├── NewLinkDialog.vue │ │ ├── NoFilesSection.vue │ │ ├── PrimaryDropdown.vue │ │ ├── ProgressRing.vue │ │ ├── RenameDialog.vue │ │ ├── SaasLoginBox.vue │ │ ├── SearchPopup.vue │ │ ├── Settings │ │ │ ├── EditTagDialog.vue │ │ │ ├── NewTagDialog.vue │ │ │ ├── ProfileSettings.vue │ │ │ ├── SettingsDialog.vue │ │ │ ├── StorageSettings.vue │ │ │ ├── TagSettings.vue │ │ │ └── UserListSettings.vue │ │ ├── ShareDialog │ │ │ ├── AccessButton.vue │ │ │ ├── ShareDialog.vue │ │ │ ├── UserAutoComplete.vue │ │ │ └── UserSearch.vue │ │ ├── ShortcutsDialog.vue │ │ ├── Sidebar.vue │ │ ├── SidebarItem.vue │ │ ├── StorageBar.vue │ │ ├── Tag.vue │ │ ├── TagColorInput.vue │ │ ├── TagInput.vue │ │ ├── TeamSwitcher.vue │ │ ├── TiptapInput.vue │ │ ├── Toast.vue │ │ ├── UploadTracker.vue │ │ ├── UserDropdown.vue │ │ ├── UsersBar.vue │ │ ├── VideoPlayer.vue │ │ ├── arrow-down-to-line-off.vue │ │ └── message-circle-off.vue │ ├── emitter.js │ ├── index.css │ ├── main.js │ ├── pages │ │ ├── Document.vue │ │ ├── Favourites.vue │ │ ├── File.vue │ │ ├── Folder.vue │ │ ├── LoginSignup.vue │ │ ├── Notifications.vue │ │ ├── Personal.vue │ │ ├── Recents.vue │ │ ├── Setup.vue │ │ ├── Shared.vue │ │ ├── Team.vue │ │ ├── Teams.vue │ │ └── Trash.vue │ ├── resources │ │ ├── files.js │ │ └── permissions.js │ ├── router.js │ ├── socket.ts │ ├── store.js │ ├── translation.js │ └── utils │ │ ├── chunkFileUpload.js │ │ ├── disable-scroll.ts │ │ ├── download.js │ │ ├── dragSelect.js │ │ ├── file-to-base64.js │ │ ├── files.js │ │ ├── format.js │ │ ├── fuzzySearcher.js │ │ ├── getIconUrl.js │ │ ├── getLink.js │ │ ├── helpers.ts │ │ ├── markdown.js │ │ ├── random-color.js │ │ ├── theme.js │ │ └── toasts.js ├── tailwind.config.js ├── vite.config.js └── yarn.lock ├── package-lock.json ├── package.json ├── pyproject.toml ├── realtime └── handlers.js ├── requirements.txt ├── setup.py └── yarn.lock /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Run code through Prettier added pre-commit formatting using husky and pretty-quick added eslint rules check package.json 2 | c2d5eeb0e8b9944cc664bbbefe211563c54ee9b7 3 | # Black on python 4 | 6795d6cc3cc16a0a81846734abc807077363a4ac 5 | # Formatting 6 | 4e6133b9693857fd507372a8fab5d3c58577a1b3 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. (Screenshots of errors, console, configuration changes, docker, bare-metal etc.) 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea to improve Frappe Drive 4 | labels: feature-request 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general_query.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question about using Frappe Drive 3 | about: This is not the appropriate channel 4 | labels: query 5 | --- 6 | 7 | General non technical queries are better handled at [discuss.frappe.io](https://discuss.frappe.io/c/frappe-drive/80). Be it about design, installation, configuration, or customization. All of it is better on the forum. 8 | 9 | For documentation, use the [Frappe Drive Documentation](https://docs.frappe.io/drive) 10 | -------------------------------------------------------------------------------- /.github/file-preview-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/file-preview-modified.png -------------------------------------------------------------------------------- /.github/file_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/file_preview.png -------------------------------------------------------------------------------- /.github/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/grid.png -------------------------------------------------------------------------------- /.github/grid_context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/grid_context.png -------------------------------------------------------------------------------- /.github/home-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/home-modified.png -------------------------------------------------------------------------------- /.github/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/list.png -------------------------------------------------------------------------------- /.github/new_logo.svg: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /.github/og_1200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/og_1200.png -------------------------------------------------------------------------------- /.github/search-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/search-modified.png -------------------------------------------------------------------------------- /.github/share-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/share-modified.png -------------------------------------------------------------------------------- /.github/share_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/share_menu.png -------------------------------------------------------------------------------- /.github/text_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/text_editor.png -------------------------------------------------------------------------------- /.github/text_editor_realtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/text_editor_realtime.png -------------------------------------------------------------------------------- /.github/upload-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/.github/upload-modified.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.egg-info 4 | *.swp 5 | tags 6 | drive/docs/current 7 | drive/public/frontend 8 | node_modules 9 | frontend/node_modules 10 | drive/public/node_modules 11 | drive/www/drive.html 12 | .yarn-integrity 13 | **/__pycache__ 14 | node_modules/ 15 | experiments 16 | *.sql -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frappe-ui"] 2 | path = frappe-ui 3 | url = https://github.com/frappe/frappe-ui 4 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include drive *.css 8 | recursive-include drive *.csv 9 | recursive-include drive *.html 10 | recursive-include drive *.ico 11 | recursive-include drive *.js 12 | recursive-include drive *.json 13 | recursive-include drive *.md 14 | recursive-include drive *.png 15 | recursive-include drive *.py 16 | recursive-include drive *.svg 17 | recursive-include drive *.txt 18 | recursive-exclude drive *.pyc -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | mariadb: 4 | image: mariadb:10.8 5 | command: 6 | - --character-set-server=utf8mb4 7 | - --collation-server=utf8mb4_unicode_ci 8 | - --skip-character-set-client-handshake 9 | - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 10 | environment: 11 | MYSQL_ROOT_PASSWORD: 123 12 | volumes: 13 | - mariadb-data:/var/lib/mysql 14 | 15 | redis: 16 | image: redis:alpine 17 | 18 | frappe: 19 | image: frappe/bench:latest 20 | command: bash /workspace/init.sh 21 | environment: 22 | - SHELL=/bin/bash 23 | working_dir: /home/frappe 24 | volumes: 25 | - .:/workspace 26 | ports: 27 | - 8000:8000 28 | - 9000:9000 29 | 30 | volumes: 31 | mariadb-data: 32 | -------------------------------------------------------------------------------- /docker/init.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | if [ -d "/home/frappe/frappe-bench/apps/frappe" ]; then 4 | echo "Bench already exists, skipping init" 5 | cd frappe-bench 6 | bench start 7 | else 8 | echo "Creating new bench..." 9 | fi 10 | 11 | bench init --skip-redis-config-generation frappe-bench --version version-15 12 | 13 | cd frappe-bench 14 | 15 | # Use containers instead of localhost 16 | bench set-mariadb-host mariadb 17 | bench set-redis-cache-host redis:6379 18 | bench set-redis-queue-host redis:6379 19 | bench set-redis-socketio-host redis:6379 20 | 21 | # Remove redis, watch from Procfile 22 | sed -i '/redis/d' ./Procfile 23 | sed -i '/watch/d' ./Procfile 24 | 25 | bench get-app drive --branch main 26 | 27 | bench new-site drive.localhost \ 28 | --force \ 29 | --mariadb-root-password 123 \ 30 | --admin-password admin \ 31 | --no-mariadb-socket 32 | 33 | bench --site drive.localhost install-app drive 34 | bench --site drive.localhost set-config developer_mode 1 35 | bench --site drive.localhost clear-cache 36 | bench --site drive.localhost set-config mute_emails 1 37 | bench use drive.localhost 38 | 39 | bench start -------------------------------------------------------------------------------- /drive/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.1" 2 | -------------------------------------------------------------------------------- /drive/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/api/__init__.py -------------------------------------------------------------------------------- /drive/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/config/__init__.py -------------------------------------------------------------------------------- /drive/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return [ 6 | { 7 | "module_name": "Drive", 8 | "color": "grey", 9 | "icon": "octicon octicon-file-directory", 10 | "type": "module", 11 | "label": _("Drive"), 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /drive/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | 6 | def get_context(context): 7 | context.brand_html = "Drive" 8 | -------------------------------------------------------------------------------- /drive/drive/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/account_request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/account_request/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/account_request/account_request.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Account Request", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/account_request/test_account_request.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestAccountRequest(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_document/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document/drive_document.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Document", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document/drive_document.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveDocument(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document/test_drive_document.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveDocument(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document_version/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_document_version/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document_version/drive_document_version.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Document Version", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document_version/drive_document_version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveDocumentVersion(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_document_version/test_drive_document_version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveDocumentVersion(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_activity_log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_entity_activity_log/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_activity_log/drive_entity_activity_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Entity Activity Log", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_activity_log/drive_entity_activity_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveEntityActivityLog(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_activity_log/patches/initialize_creation.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | all_entities = frappe.db.get_list("Drive File", fields=["name", "title", "owner", "creation"]) 6 | 7 | for i in all_entities: 8 | doc = frappe.new_doc("Drive Entity Activity Log") 9 | doc.entity = i.name 10 | doc.action_type = "create" 11 | doc.message = f"Created {i.title}" 12 | doc.save() 13 | frappe.db.set_value("Drive Entity Activity Log", doc.name, "owner", i.owner) 14 | frappe.db.set_value("Drive Entity Activity Log", doc.name, "creation", i.creation) 15 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_activity_log/test_drive_entity_activity_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveEntityActivityLog(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_entity_log/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_log/drive_entity_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Entity Log", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_log/drive_entity_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveEntityLog(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_log/test_drive_entity_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveEntityLog(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_tag/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_entity_tag/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_tag/drive_entity_tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2023-03-08 23:57:09.297424", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": ["tag"], 8 | "fields": [ 9 | { 10 | "fieldname": "tag", 11 | "fieldtype": "Link", 12 | "label": "Tag", 13 | "options": "Drive Tag" 14 | } 15 | ], 16 | "index_web_pages_for_search": 1, 17 | "istable": 1, 18 | "links": [], 19 | "modified": "2023-08-23 17:37:09.877214", 20 | "modified_by": "Administrator", 21 | "module": "Drive", 22 | "name": "Drive Entity Tag", 23 | "owner": "Administrator", 24 | "permissions": [], 25 | "sort_field": "modified", 26 | "sort_order": "DESC", 27 | "states": [] 28 | } 29 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_entity_tag/drive_entity_tag.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveEntityTag(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_favourite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_favourite/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_favourite/drive_favourite.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Favourite", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_favourite/drive_favourite.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "autoincrement", 4 | "creation": "2022-11-25 11:51:10.412352", 5 | "doctype": "DocType", 6 | "engine": "InnoDB", 7 | "field_order": ["user", "entity"], 8 | "fields": [ 9 | { 10 | "fieldname": "user", 11 | "fieldtype": "Link", 12 | "in_list_view": 1, 13 | "label": "User", 14 | "options": "User", 15 | "reqd": 1 16 | }, 17 | { 18 | "fieldname": "entity", 19 | "fieldtype": "Link", 20 | "in_list_view": 1, 21 | "label": "Drive File", 22 | "options": "Drive File", 23 | "reqd": 1 24 | } 25 | ], 26 | "links": [], 27 | "modified": "2025-02-13 15:11:38.610661", 28 | "modified_by": "Administrator", 29 | "module": "Drive", 30 | "name": "Drive Favourite", 31 | "naming_rule": "Autoincrement", 32 | "owner": "Administrator", 33 | "permissions": [ 34 | { 35 | "create": 1, 36 | "delete": 1, 37 | "email": 1, 38 | "export": 1, 39 | "print": 1, 40 | "read": 1, 41 | "report": 1, 42 | "role": "All", 43 | "share": 1, 44 | "write": 1 45 | } 46 | ], 47 | "sort_field": "modified", 48 | "sort_order": "DESC", 49 | "states": [] 50 | } 51 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_favourite/drive_favourite.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveFavourite(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_favourite/test_drive_favourite.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveFavourite(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_file/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_file/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_file/drive_file.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Drive File", {}); 5 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_file/test_drive_file.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | 8 | # On IntegrationTestCase, the doctype test records and all 9 | # link-field test record dependencies are recursively loaded 10 | # Use these module variables to add/remove to/from that list 11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 13 | 14 | 15 | class UnitTestDriveFile(UnitTestCase): 16 | """ 17 | Unit tests for DriveFile. 18 | Use this class for testing individual functions and methods. 19 | """ 20 | 21 | pass 22 | 23 | 24 | class IntegrationTestDriveFile(IntegrationTestCase): 25 | """ 26 | Integration tests for DriveFile. 27 | Use this class for testing interactions between multiple components. 28 | """ 29 | 30 | pass 31 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_notification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_notification/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_notification/drive_notification.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Notification", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_notification/drive_notification.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveNotification(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_notification/test_drive_notification.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveNotification(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_permission/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_permission/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_permission/drive_permission.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Permission", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_permission/drive_permission.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | from drive.api.notifications import notify_share 7 | 8 | 9 | class DrivePermission(Document): 10 | def after_insert(self): 11 | if self.user: 12 | frappe.enqueue( 13 | notify_share, 14 | queue="long", 15 | job_id=f"fdocperm_{self.name}", 16 | deduplicate=True, 17 | timeout=None, 18 | now=False, 19 | at_front=False, 20 | entity_name=self.entity, 21 | docperm_name=self.name, 22 | ) 23 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_permission/test_drive_permission.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | 8 | # On IntegrationTestCase, the doctype test records and all 9 | # link-field test record dependencies are recursively loaded 10 | # Use these module variables to add/remove to/from that list 11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 13 | 14 | 15 | class UnitTestDrivePermission(UnitTestCase): 16 | """ 17 | Unit tests for DrivePermission. 18 | Use this class for testing individual functions and methods. 19 | """ 20 | 21 | pass 22 | 23 | 24 | class IntegrationTestDrivePermission(IntegrationTestCase): 25 | """ 26 | Integration tests for DrivePermission. 27 | Use this class for testing interactions between multiple components. 28 | """ 29 | 30 | pass 31 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_s3_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_s3_settings/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_s3_settings/drive_s3_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive S3 Settings", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_s3_settings/drive_s3_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveS3Settings(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_s3_settings/test_drive_s3_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | 8 | # On IntegrationTestCase, the doctype test records and all 9 | # link-field test record dependencies are recursively loaded 10 | # Use these module variables to add/remove to/from that list 11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 13 | 14 | 15 | class UnitTestDriveS3Settings(UnitTestCase): 16 | """ 17 | Unit tests for DriveS3Settings. 18 | Use this class for testing individual functions and methods. 19 | """ 20 | 21 | pass 22 | 23 | 24 | class IntegrationTestDriveS3Settings(IntegrationTestCase): 25 | """ 26 | Integration tests for DriveS3Settings. 27 | Use this class for testing interactions between multiple components. 28 | """ 29 | 30 | pass 31 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_settings/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_settings/drive_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Settings", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_settings/drive_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveSettings(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_settings/test_drive_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | 8 | # On IntegrationTestCase, the doctype test records and all 9 | # link-field test record dependencies are recursively loaded 10 | # Use these module variables to add/remove to/from that list 11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 13 | 14 | 15 | class UnitTestDriveSettings(UnitTestCase): 16 | """ 17 | Unit tests for DriveSettings. 18 | Use this class for testing individual functions and methods. 19 | """ 20 | 21 | pass 22 | 23 | 24 | class IntegrationTestDriveSettings(IntegrationTestCase): 25 | """ 26 | Integration tests for DriveSettings. 27 | Use this class for testing interactions between multiple components. 28 | """ 29 | 30 | pass 31 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_site_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_site_settings/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_site_settings/drive_site_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Site Settings", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_site_settings/drive_site_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2025-05-27 14:25:36.760538", 5 | "doctype": "DocType", 6 | "engine": "InnoDB", 7 | "field_order": ["jwt_key"], 8 | "fields": [ 9 | { 10 | "description": "Never share this publicly! This gives complete read access to all files in your site.", 11 | "fieldname": "jwt_key", 12 | "fieldtype": "Password", 13 | "in_list_view": 1, 14 | "label": "JWT Key", 15 | "reqd": 1 16 | } 17 | ], 18 | "grid_page_length": 50, 19 | "index_web_pages_for_search": 1, 20 | "issingle": 1, 21 | "links": [], 22 | "modified": "2025-05-30 15:23:21.155154", 23 | "modified_by": "Administrator", 24 | "module": "Drive", 25 | "name": "Drive Site Settings", 26 | "owner": "Administrator", 27 | "permissions": [ 28 | { 29 | "create": 1, 30 | "delete": 1, 31 | "email": 1, 32 | "print": 1, 33 | "read": 1, 34 | "role": "System Manager", 35 | "share": 1, 36 | "write": 1 37 | } 38 | ], 39 | "row_format": "Dynamic", 40 | "sort_field": "creation", 41 | "sort_order": "DESC", 42 | "states": [] 43 | } 44 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_site_settings/drive_site_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveSiteSettings(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_site_settings/test_drive_site_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | 8 | # On IntegrationTestCase, the doctype test records and all 9 | # link-field test record dependencies are recursively loaded 10 | # Use these module variables to add/remove to/from that list 11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 13 | 14 | 15 | class UnitTestDriveSiteSettings(UnitTestCase): 16 | """ 17 | Unit tests for DriveSiteSettings. 18 | Use this class for testing individual functions and methods. 19 | """ 20 | 21 | pass 22 | 23 | 24 | class IntegrationTestDriveSiteSettings(IntegrationTestCase): 25 | """ 26 | Integration tests for DriveSiteSettings. 27 | Use this class for testing interactions between multiple components. 28 | """ 29 | 30 | pass 31 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_tag/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_tag/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_tag/drive_tag.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Tag", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_tag/drive_tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2023-03-08 23:56:05.264166", 5 | "default_view": "List", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": ["title", "color"], 10 | "fields": [ 11 | { 12 | "fieldname": "title", 13 | "fieldtype": "Data", 14 | "in_list_view": 1, 15 | "label": "Title", 16 | "reqd": 1 17 | }, 18 | { 19 | "fieldname": "color", 20 | "fieldtype": "Data", 21 | "label": "Color" 22 | } 23 | ], 24 | "links": [], 25 | "modified": "2025-03-05 17:10:23.811211", 26 | "modified_by": "Administrator", 27 | "module": "Drive", 28 | "name": "Drive Tag", 29 | "owner": "Administrator", 30 | "permissions": [ 31 | { 32 | "create": 1, 33 | "export": 1, 34 | "read": 1, 35 | "role": "All", 36 | "share": 1, 37 | "write": 1 38 | } 39 | ], 40 | "sort_field": "modified", 41 | "sort_order": "DESC", 42 | "states": [] 43 | } 44 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_tag/drive_tag.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveTag(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_tag/test_drive_tag.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveTag(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_team/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_team/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_team/drive_team.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive Team", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_team/test_drive_team.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | 8 | # On IntegrationTestCase, the doctype test records and all 9 | # link-field test record dependencies are recursively loaded 10 | # Use these module variables to add/remove to/from that list 11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 13 | 14 | 15 | class UnitTestDriveTeam(UnitTestCase): 16 | """ 17 | Unit tests for DriveTeam. 18 | Use this class for testing individual functions and methods. 19 | """ 20 | 21 | pass 22 | 23 | 24 | class IntegrationTestDriveTeam(IntegrationTestCase): 25 | """ 26 | Integration tests for DriveTeam. 27 | Use this class for testing interactions between multiple components. 28 | """ 29 | 30 | pass 31 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_team_member/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_team_member/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_team_member/drive_team_member.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2025-01-23 13:08:15.285320", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": ["user", "is_admin"], 9 | "fields": [ 10 | { 11 | "fieldname": "user", 12 | "fieldtype": "Link", 13 | "in_list_view": 1, 14 | "label": "User", 15 | "options": "User" 16 | }, 17 | { 18 | "default": "0", 19 | "fieldname": "is_admin", 20 | "fieldtype": "Check", 21 | "label": "Is Admin" 22 | } 23 | ], 24 | "index_web_pages_for_search": 1, 25 | "istable": 1, 26 | "links": [], 27 | "modified": "2025-02-28 11:26:24.271128", 28 | "modified_by": "Administrator", 29 | "module": "Drive", 30 | "name": "Drive Team Member", 31 | "owner": "Administrator", 32 | "permissions": [], 33 | "sort_field": "creation", 34 | "sort_order": "DESC", 35 | "states": [] 36 | } 37 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_team_member/drive_team_member.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class DriveTeamMember(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_user_invitation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/drive/doctype/drive_user_invitation/__init__.py -------------------------------------------------------------------------------- /drive/drive/doctype/drive_user_invitation/drive_user_invitation.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Drive User Invitation", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /drive/drive/doctype/drive_user_invitation/test_drive_user_invitation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestDriveUserInvitation(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /drive/install.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def after_install(): 5 | index_check = frappe.db.sql( 6 | """SHOW INDEX FROM `tabDrive File` WHERE Key_name = 'drive_file_title_fts_idx'""" 7 | ) 8 | if not index_check: 9 | frappe.db.sql( 10 | """ALTER TABLE `tabDrive File` ADD FULLTEXT INDEX drive_file_title_fts_idx (title)""" 11 | ) 12 | -------------------------------------------------------------------------------- /drive/modules.txt: -------------------------------------------------------------------------------- 1 | Drive -------------------------------------------------------------------------------- /drive/patches.txt: -------------------------------------------------------------------------------- 1 | [pre_model_sync] 2 | drive.patches.team_restructure #8 3 | 4 | [post_model_sync] 5 | drive.patches.folder_size #3 6 | drive.patches.settings 7 | -------------------------------------------------------------------------------- /drive/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/patches/__init__.py -------------------------------------------------------------------------------- /drive/patches/folder_size.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def scan(folder): 5 | folder = frappe.get_doc("Drive File", folder) 6 | child_folders = frappe.get_list( 7 | "Drive File", {"parent_entity": folder.name, "is_group": 1}, pluck="name" 8 | ) 9 | for child in child_folders: 10 | scan(child) 11 | sizes = frappe.get_list( 12 | "Drive File", {"parent_entity": folder.name, "is_active": 1}, pluck="file_size" 13 | ) 14 | frappe.db.set_value("Drive File", folder.name, "file_size", sum(sizes), update_modified=False) 15 | 16 | 17 | def execute(): 18 | roots = frappe.get_list("Drive File", {"parent_entity": ""}, pluck="name") 19 | for root in roots: 20 | scan(root) 21 | -------------------------------------------------------------------------------- /drive/patches/settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | for user in frappe.db.get_list("User", pluck="name"): 6 | teams = frappe.get_all( 7 | "Drive Team Member", 8 | pluck="parent", 9 | filters=[ 10 | ["parenttype", "=", "Drive Team"], 11 | ["user", "=", user], 12 | ], 13 | ) 14 | if teams: 15 | if not frappe.db.exists("Drive Settings", {"user": user}): 16 | frappe.get_doc( 17 | { 18 | "doctype": "Drive Settings", 19 | "user": user, 20 | "single_click": 1, 21 | "default_team": teams[0], 22 | } 23 | ).insert() 24 | -------------------------------------------------------------------------------- /drive/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/public/.gitkeep -------------------------------------------------------------------------------- /drive/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/templates/__init__.py -------------------------------------------------------------------------------- /drive/templates/emails/drive_invitation.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | You've been invited to join
4 | {{ team_name }} on Frappe Drive! 5 |

6 | 7 |

10 | {{ user }} has invited to you to join {{ team_name }} on 11 | Frappe Drive. Click below to get started. 12 |

13 | 14 |

15 | 27 | Accept Invitation 28 | 29 |

30 | 31 |

32 | If you didn’t request this, feel free to ignore this email. 33 |

34 |
35 | -------------------------------------------------------------------------------- /drive/templates/emails/drive_share.html: -------------------------------------------------------------------------------- 1 |
2 |

Share from {{ team_name }}

3 | 4 |

7 | {{message}} 8 |

9 | 10 |

11 | 23 | Open {{ type }} 24 | 25 |

26 | 27 |

28 | If you didn’t request this, feel free to ignore this email. 29 |

30 |
31 | -------------------------------------------------------------------------------- /drive/templates/emails/otp.html: -------------------------------------------------------------------------------- 1 |
2 |

Sign in to your account:

3 | 4 | 5 |
8 |

{{ otp }}

9 |
10 | 11 |

12 | If you didn’t request this, feel free to ignore this email. 13 |

14 |
15 | -------------------------------------------------------------------------------- /drive/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/utils/__init__.py -------------------------------------------------------------------------------- /drive/utils/dev.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from time import time 3 | 4 | 5 | def timing(f): 6 | @wraps(f) 7 | def wrap(*args, **kw): 8 | ts = time() 9 | result = f(*args, **kw) 10 | te = time() 11 | print( 12 | " %2.4f s: func:%r " 13 | % ( 14 | te - ts, 15 | f.__name__, 16 | ) 17 | ) 18 | return result 19 | 20 | return wrap 21 | -------------------------------------------------------------------------------- /drive/www/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/drive/www/__init__.py -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | }, 5 | extends: ["eslint:recommended", "plugin:vue/vue3-recommended", "prettier"], 6 | rules: { 7 | // override/add rules settings here, such as: 8 | // 'vue/no-unused-vars': 'error' 9 | // "vue/require-default-prop": "off", 10 | // Frappe ui 11 | "vue/no-reserved-component-names": "off", 12 | "vue/multi-word-component-names": "off", 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /frontend/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | ../../env/bin/black drive/ 5 | 6 | cd frontend/ 7 | 8 | npm run precommit 9 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false 3 | } 4 | -------------------------------------------------------------------------------- /frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "jsx": "react" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #ffffff 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/public/color-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/color-circle.png -------------------------------------------------------------------------------- /frontend/public/favicon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-114x114.png -------------------------------------------------------------------------------- /frontend/public/favicon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-120x120.png -------------------------------------------------------------------------------- /frontend/public/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-128x128.png -------------------------------------------------------------------------------- /frontend/public/favicon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-144x144.png -------------------------------------------------------------------------------- /frontend/public/favicon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-150x150.png -------------------------------------------------------------------------------- /frontend/public/favicon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-152x152.png -------------------------------------------------------------------------------- /frontend/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/public/favicon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-180x180.png -------------------------------------------------------------------------------- /frontend/public/favicon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-192x192.png -------------------------------------------------------------------------------- /frontend/public/favicon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-310x310.png -------------------------------------------------------------------------------- /frontend/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-32x32.png -------------------------------------------------------------------------------- /frontend/public/favicon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-384x384.png -------------------------------------------------------------------------------- /frontend/public/favicon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-512x512.png -------------------------------------------------------------------------------- /frontend/public/favicon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-57x57.png -------------------------------------------------------------------------------- /frontend/public/favicon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-60x60.png -------------------------------------------------------------------------------- /frontend/public/favicon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-70x70.png -------------------------------------------------------------------------------- /frontend/public/favicon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-72x72.png -------------------------------------------------------------------------------- /frontend/public/favicon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-76x76.png -------------------------------------------------------------------------------- /frontend/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon-96x96.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/drive/e052a5318b6ef0338a4d09c44fa0f13517116023/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Frappe Drive", 3 | "description": "An easy to use, document sharing and management solution", 4 | "short_name": "Drive", 5 | "icons": [ 6 | { 7 | "src": "/favicon-72x72.png", 8 | "type": "image/png", 9 | "sizes": "72x72", 10 | "purpose": "any maskable" 11 | }, 12 | { 13 | "src": "/favicon-96x96.png", 14 | "type": "image/png", 15 | "sizes": "96x96", 16 | "purpose": "any maskable" 17 | }, 18 | { 19 | "src": "/favicon-128x128.png", 20 | "type": "image/png", 21 | "sizes": "128x128", 22 | "purpose": "any maskable" 23 | }, 24 | { 25 | "src": "/favicon-144x144.png", 26 | "type": "image/png", 27 | "sizes": "144x144", 28 | "purpose": "any maskable" 29 | }, 30 | { 31 | "src": "/favicon-152x152.png", 32 | "type": "image/png", 33 | "sizes": "152x152", 34 | "purpose": "any maskable" 35 | }, 36 | { 37 | "src": "/favicon-192x192.png", 38 | "type": "image/png", 39 | "sizes": "192x192", 40 | "purpose": "any maskable" 41 | }, 42 | { 43 | "src": "/favicon-384x384.png", 44 | "type": "image/png", 45 | "sizes": "384x384", 46 | "purpose": "any maskable" 47 | }, 48 | { 49 | "src": "/favicon-512x512.png", 50 | "type": "image/png", 51 | "sizes": "512x512", 52 | "purpose": "any maskable" 53 | } 54 | ], 55 | "scope": "/", 56 | "start_url": "/", 57 | "display": "standalone", 58 | "theme_color": "#ffffff", 59 | "background_color": "#ffffff" 60 | } 61 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/Link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/after effects.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/application.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/archive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/audio.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/excel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/markdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/page-break.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/photoshop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/presentation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/shared-folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/sketch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/spreadsheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/video.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/assets/images/icons/word.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/components/NewManualSnapshotDialog.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 39 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/DetailsExtension/DetailsExtension/details-item.ts: -------------------------------------------------------------------------------- 1 | import { mergeAttributes, Node } from "@tiptap/core" 2 | 3 | export interface DetailContentOptions { 4 | readonly HTMLAttributes: Record 5 | } 6 | 7 | export const DetailsContent = Node.create({ 8 | name: `detailsContent`, 9 | 10 | content: `block+`, 11 | 12 | group: `block`, 13 | 14 | allowGapCursor: true, 15 | 16 | parseHTML() { 17 | return [ 18 | { 19 | tag: `div[data-type="details-content"]`, 20 | }, 21 | ] 22 | }, 23 | 24 | renderHTML({ HTMLAttributes }) { 25 | return [ 26 | `div`, 27 | mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { 28 | "data-type": `details-content`, 29 | }), 30 | 0, 31 | ] 32 | }, 33 | }) 34 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/DetailsExtension/DetailsExtension/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./details" 2 | export * from "./details-item" 3 | export * from "./summary" 4 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/DetailsExtension/DetailsExtension/summary.ts: -------------------------------------------------------------------------------- 1 | import { mergeAttributes, Node } from "@tiptap/core" 2 | 3 | export interface SummaryOptions { 4 | readonly HTMLAttributes: Record 5 | } 6 | 7 | export const DetailsSummary = Node.create({ 8 | name: `summary`, 9 | 10 | addOptions() { 11 | return { 12 | HTMLAttributes: {}, 13 | } 14 | }, 15 | 16 | content: `paragraph`, 17 | 18 | group: `block`, 19 | 20 | parseHTML() { 21 | return [ 22 | { 23 | tag: `summary`, 24 | }, 25 | ] 26 | }, 27 | 28 | renderHTML({ HTMLAttributes }) { 29 | return [ 30 | `summary`, 31 | mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 32 | 0, 33 | ] 34 | }, 35 | }) 36 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/InsDelMark.ts: -------------------------------------------------------------------------------- 1 | import { Mark, mergeAttributes } from "@tiptap/core" 2 | 3 | export const Ins = Mark.create({ 4 | name: "ins", 5 | parseHTML() { 6 | return [ 7 | { 8 | tag: "ins", 9 | priority: 600, 10 | getAttrs: (node) => { 11 | if (node.closest("code, table")) { 12 | return false 13 | } 14 | return true 15 | }, 16 | }, 17 | ] 18 | }, 19 | renderHTML({ HTMLAttributes }) { 20 | return ["ins", mergeAttributes(HTMLAttributes), 0] 21 | }, 22 | }) 23 | 24 | export const Del = Mark.create({ 25 | name: "del", 26 | parseHTML() { 27 | return [ 28 | { 29 | tag: "del", 30 | priority: 600, 31 | getAttrs: (node) => { 32 | if (node.closest("code, table")) { 33 | return false 34 | } 35 | return true 36 | }, 37 | }, 38 | ] 39 | }, 40 | renderHTML({ HTMLAttributes }) { 41 | return ["del", mergeAttributes(HTMLAttributes), 0] 42 | }, 43 | }) 44 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/InsDelNode.ts: -------------------------------------------------------------------------------- 1 | import { Node, mergeAttributes } from "@tiptap/core" 2 | 3 | export const InsNode = Node.create({ 4 | name: "ins", 5 | inline: true, 6 | group: "inline*", 7 | parseHTML() { 8 | return [{ tag: "ins" }] 9 | }, 10 | renderHTML({ HTMLAttributes }) { 11 | return ["ins", mergeAttributes(HTMLAttributes), 0] 12 | }, 13 | addAttributes() { 14 | return { 15 | class: { 16 | default: null, 17 | }, 18 | } 19 | }, 20 | }) 21 | 22 | export const DelNode = Node.create({ 23 | name: "del", 24 | inline: true, 25 | group: "inline*", 26 | parseHTML() { 27 | return [{ tag: "del" }] 28 | }, 29 | renderHTML({ HTMLAttributes }) { 30 | return ["del", mergeAttributes(HTMLAttributes), 0] 31 | }, 32 | addAttributes() { 33 | return { 34 | class: { 35 | default: null, 36 | }, 37 | } 38 | }, 39 | }) 40 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/createDiffMark.ts: -------------------------------------------------------------------------------- 1 | import { Mark, mergeAttributes } from "@tiptap/core" 2 | import { DiffType } from "./diffType" 3 | 4 | export const DiffMarkExtension = Mark.create({ 5 | name: "diffMark", 6 | 7 | addAttributes() { 8 | return { 9 | type: { 10 | renderHTML: ({ type }) => { 11 | const color = { 12 | [DiffType.Inserted]: "#bcf5bc", 13 | [DiffType.Deleted]: "#ff8989", 14 | }[type] 15 | return { 16 | style: "background-color: " + color, 17 | } 18 | }, 19 | }, 20 | } 21 | }, 22 | 23 | renderHTML({ HTMLAttributes }) { 24 | return [ 25 | "span", 26 | mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 27 | 0, 28 | ] 29 | }, 30 | }) 31 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/diffType.js: -------------------------------------------------------------------------------- 1 | export const DiffType = { 2 | Unchanged: 0, 3 | Deleted: -1, 4 | Inserted: 1, 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/document.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@tiptap/core" 2 | 3 | /** 4 | * The default document node which represents the top level node of the editor. 5 | * @see https://tiptap.dev/api/nodes/document 6 | */ 7 | export const Document = Node.create({ 8 | name: "doc", 9 | topNode: true, 10 | content: "block+", 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/paragraph.ts: -------------------------------------------------------------------------------- 1 | import { mergeAttributes, Node } from "@tiptap/core" 2 | 3 | export interface ParagraphOptions { 4 | /** 5 | * The HTML attributes for a paragraph node. 6 | * @default {} 7 | * @example { class: 'foo' } 8 | */ 9 | HTMLAttributes: Record 10 | } 11 | 12 | declare module "@tiptap/core" { 13 | interface Commands { 14 | paragraph: { 15 | /** 16 | * Toggle a paragraph 17 | * @example editor.commands.toggleParagraph() 18 | */ 19 | setParagraph: () => ReturnType 20 | } 21 | } 22 | } 23 | 24 | /** 25 | * This extension allows you to create paragraphs. 26 | * @see https://www.tiptap.dev/api/nodes/paragraph 27 | */ 28 | export const Paragraph = Node.create({ 29 | name: "paragraph", 30 | 31 | priority: 1000, 32 | 33 | addOptions() { 34 | return { 35 | HTMLAttributes: {}, 36 | } 37 | }, 38 | 39 | group: "block", 40 | 41 | content: "inline*", 42 | 43 | parseHTML() { 44 | return [{ tag: "p" }] 45 | }, 46 | 47 | renderHTML({ HTMLAttributes }) { 48 | return [ 49 | "p", 50 | mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 51 | 0, 52 | ] 53 | }, 54 | 55 | addCommands() { 56 | return { 57 | setParagraph: 58 | () => 59 | ({ commands }) => { 60 | return commands.setNode(this.name) 61 | }, 62 | } 63 | }, 64 | 65 | addKeyboardShortcuts() { 66 | return { 67 | "Mod-Alt-0": () => this.editor.commands.setParagraph(), 68 | } 69 | }, 70 | }) 71 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/resizenode/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resizableMedia" 2 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/suggestion/suggestionExtension.js: -------------------------------------------------------------------------------- 1 | import { Extension } from "@tiptap/core" 2 | import Suggestion from "@tiptap/suggestion" 3 | 4 | export default Extension.create({ 5 | name: "slash-commands", 6 | 7 | addOptions() { 8 | return { 9 | suggestion: { 10 | char: "/", 11 | command: ({ editor, range, props }) => { 12 | props.command({ editor, range }) 13 | }, 14 | }, 15 | } 16 | }, 17 | 18 | addProseMirrorPlugins() { 19 | return [ 20 | Suggestion({ 21 | editor: this.editor, 22 | ...this.options.suggestion, 23 | }), 24 | ] 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/table/index.ts: -------------------------------------------------------------------------------- 1 | import { TableHeader } from "./header" 2 | import type { TableHeaderOptions } from "./header" 3 | 4 | import { TableRow } from "./row" 5 | import type { TableRowOptions } from "@tiptap/extension-table-row" 6 | 7 | import { TableCell } from "./cell" 8 | import type { TableCellOptions } from "./cell" 9 | 10 | import { Table } from "./table" 11 | import type { TableOptions } from "./table" 12 | 13 | export { 14 | Table, 15 | TableOptions, 16 | TableCell, 17 | TableCellOptions, 18 | TableRow, 19 | TableRowOptions, 20 | TableHeader, 21 | TableHeaderOptions, 22 | TableCellBackground, 23 | TableCellBackgroundOptions, 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/table/row.ts: -------------------------------------------------------------------------------- 1 | import TiptapTableRow from "@tiptap/extension-table-row" 2 | 3 | export const TableRow = TiptapTableRow.extend({ 4 | allowGapCursor: false, 5 | }) 6 | 7 | export default TableRow 8 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/table/table.ts: -------------------------------------------------------------------------------- 1 | import TiptapTable from "@tiptap/extension-table" 2 | import TableRow from "./row" 3 | import TableHeader from "./header" 4 | import { TableCell } from "./cell" 5 | 6 | import type { TableRowOptions } from "@tiptap/extension-table-row" 7 | import type { TableHeaderOptions } from "./header" 8 | import type { TableCellOptions } from "./cell" 9 | import { GeneralOptions } from "@/type" 10 | 11 | export interface TableOptions extends GeneralOptions { 12 | HTMLAttributes: Record 13 | resizable: boolean 14 | handleWidth: number 15 | cellMinWidth: number 16 | lastColumnResizable: boolean 17 | allowTableNodeSelection: boolean 18 | /** options for table rows */ 19 | tableRow: Partial 20 | /** options for table headers */ 21 | tableHeader: Partial 22 | /** options for table cells */ 23 | tableCell: Partial 24 | /** options for table cell background */ 25 | } 26 | export const Table = TiptapTable.extend({ 27 | addOptions() { 28 | return { 29 | ...this.parent?.(), 30 | HTMLAttributes: {}, 31 | resizable: true, 32 | lastColumnResizable: true, 33 | allowTableNodeSelection: false, 34 | } 35 | }, 36 | addExtensions() { 37 | return [ 38 | TableRow.configure(this.options.tableRow), 39 | TableHeader.configure(this.options.tableHeader), 40 | TableCell.configure(this.options.tableCell), 41 | ] 42 | }, 43 | }) 44 | 45 | export default Table 46 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/extensions/text.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@tiptap/core" 2 | 3 | /** 4 | * This extension allows you to create text nodes. 5 | * @see https://www.tiptap.dev/api/nodes/text 6 | */ 7 | export const Text = Node.create({ 8 | name: "text", 9 | group: "inline", 10 | }) 11 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/AlignCenter.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/AlignJustify.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/AlignLeft.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/AlignRight.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Bold.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Check.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Details.vue: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Image.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Indent.vue: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Italic.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/List.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Mention.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/NewAnnotation.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/NewLink.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Outdent.vue: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/PageBreak.vue: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/ToggleHeaderCell.vue: -------------------------------------------------------------------------------- 1 | 56 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Underline.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/Video.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/align-center.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/align-item-center.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/align-item-left.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/align-item-right.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/align-left.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/align-right.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/delete.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/float-item-left.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/float-item-right.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/float-left.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/icons/float-right.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TextEditor.vue" 2 | export { default as TextEditor } from "./TextEditor.vue" 3 | export { default as PreviewEditor } from "./PreviewEditor.vue" 4 | export { default as TextEditorBubbleMenu } from "./TextEditorBubbleMenu.vue" 5 | export { default as TextEditorFixedMenu } from "./TextEditorFixedMenu.vue" 6 | export { default as TextEditorFloatingMenu } from "./TextEditorFloatingMenu.vue" 7 | export { EditorContent as TextEditorContent } from "@tiptap/vue-3" 8 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/utils.js: -------------------------------------------------------------------------------- 1 | import commands from "./commands" 2 | 3 | export function createEditorButton(option) { 4 | if (option instanceof Array) { 5 | return option.map(createEditorButton) 6 | } 7 | if (typeof option == "object") { 8 | return option 9 | } 10 | return commands[option] 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/utils/deleteNodes.ts: -------------------------------------------------------------------------------- 1 | import { CommandProps } from "@tiptap/core" 2 | import { EditorState } from "prosemirror-state" 3 | 4 | export function deleteNode( 5 | state: EditorState, 6 | dispatch: CommandProps["dispatch"], 7 | nodeName: string 8 | ): boolean { 9 | const position = state.selection.$anchor 10 | 11 | for (let depth = position.depth; depth > 0; depth--) { 12 | const node = position.node(depth) 13 | 14 | if (node.type.name === nodeName) { 15 | if (dispatch) { 16 | dispatch( 17 | state.tr 18 | .delete(position.before(depth), position.after(depth)) 19 | .scrollIntoView() 20 | ) 21 | } 22 | 23 | return true 24 | } 25 | } 26 | 27 | return false 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/utils/getNestedNodes.ts: -------------------------------------------------------------------------------- 1 | import { Attrs, Node as NodeElement } from "prosemirror-model" 2 | 3 | export function getNestedNodes( 4 | node: NodeElement 5 | ): Array> { 6 | const nodes: Array> = [] 7 | 8 | // @note: the content field is not array type 9 | node.content.forEach((child) => { 10 | if (child instanceof NodeElement) { 11 | nodes.push([child.type.name, child.attrs]) 12 | } 13 | }) 14 | 15 | return nodes 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/components/DocEditor/utils/getSelectedContent.ts: -------------------------------------------------------------------------------- 1 | import { getHTMLFromFragment } from "@tiptap/core" 2 | import { EditorState } from "prosemirror-state" 3 | 4 | export function getSelectedContent( 5 | state: EditorState, 6 | current?: string 7 | ): string { 8 | const currentNodeContent = current ?? state.selection.$head.parent.textContent 9 | const selected = state.doc.cut(state.selection.from, state.selection.to) 10 | 11 | return selected.content.size 12 | ? getHTMLFromFragment(selected.content, state.schema) 13 | : currentNodeContent 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Check.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/ChevronDown.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Clock.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Cloud.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Colour-picker.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 41 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Comment.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Diamond.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/DownArrow.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Download.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Edit.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/File-upload.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/File.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Filter.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Folder-upload.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Folder.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Home.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Info.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Link.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Lock.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Move-2.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Move.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/MyDrive.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/NewFile.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/NewFolder.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Open.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Preview.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Printer.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Recent.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Rename.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Search.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Share.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/ShareNew.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Sort.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/Star.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/User.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/View.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/ViewGrid.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/EspressoIcons/ViewList.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/FilePreview.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | -------------------------------------------------------------------------------- /frontend/src/components/FileTypePreview/PDFPreview.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /frontend/src/components/FolderContentsError.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 34 | -------------------------------------------------------------------------------- /frontend/src/components/FrappeDriveLogo.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /frontend/src/components/FrappeFileLine.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/FrappeFolder.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /frontend/src/components/FrappeFolderLine.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/FrappeLogo.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/Loader.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Audio.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Document.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Folder.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Image.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Presentation.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Spreadsheet.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Unknown.vue: -------------------------------------------------------------------------------- 1 | 42 | -------------------------------------------------------------------------------- /frontend/src/components/MimeIcons/Video.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/MobileSidebar.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 42 | -------------------------------------------------------------------------------- /frontend/src/components/NoFilesSection.vue: -------------------------------------------------------------------------------- 1 | 24 | 43 | -------------------------------------------------------------------------------- /frontend/src/components/ProgressRing.vue: -------------------------------------------------------------------------------- 1 | 24 | 62 | -------------------------------------------------------------------------------- /frontend/src/components/ShareDialog/AccessButton.vue: -------------------------------------------------------------------------------- 1 | 16 | 25 | -------------------------------------------------------------------------------- /frontend/src/components/VideoPlayer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | -------------------------------------------------------------------------------- /frontend/src/components/arrow-down-to-line-off.vue: -------------------------------------------------------------------------------- 1 | 39 | -------------------------------------------------------------------------------- /frontend/src/components/message-circle-off.vue: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /frontend/src/emitter.js: -------------------------------------------------------------------------------- 1 | import mitt from "mitt" 2 | 3 | const emitter = mitt() 4 | export default emitter 5 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | /* frappe ui includes Inter*/ 2 | @import "frappe-ui/src/style.css"; 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | * { 8 | scrollbar-width: thin; 9 | scrollbar-color: #c0c6cc #ebeef0; 10 | } 11 | 12 | html { 13 | scrollbar-width: auto; 14 | } 15 | 16 | *::-webkit-scrollbar-thumb { 17 | background: #c0c6cc; 18 | border-radius: 6px; 19 | } 20 | 21 | *::-webkit-scrollbar-track, 22 | *::-webkit-scrollbar-corner { 23 | background: #ebeef0; 24 | } 25 | 26 | *::-webkit-scrollbar { 27 | width: 6px; 28 | height: 6px; 29 | } 30 | 31 | body::-webkit-scrollbar { 32 | width: 12px; 33 | height: 12px; 34 | } 35 | 36 | body { 37 | overflow: hidden; 38 | } 39 | 40 | .fade-in-enter-active { 41 | transition: opacity 300ms cubic-bezier(0.55, 0.085, 0.68, 0.53); 42 | } 43 | 44 | .fade-in-leave-active { 45 | transition: opacity 225ms cubic-bezier(0.25, 0.46, 0.45, 0.94); 46 | } 47 | 48 | .fade-in-enter, 49 | .fade-in-leave-to { 50 | opacity: 0; 51 | } 52 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue" 2 | import { 3 | FrappeUI, 4 | Button, 5 | onOutsideClickDirective, 6 | setConfig, 7 | frappeRequest, 8 | } from "frappe-ui" 9 | import store from "./store" 10 | import translationPlugin from "./translation" 11 | import router from "./router" 12 | import App from "./App.vue" 13 | import emitter from "@/emitter" 14 | import "./index.css" 15 | import VueTippy from "vue-tippy" 16 | import { initSocket, RealTimeHandler } from "./socket" 17 | 18 | const app = createApp(App) 19 | setConfig("resourceFetcher", frappeRequest) 20 | app.config.unwrapInjectedRef = true 21 | app.config.globalProperties.emitter = emitter 22 | app.provide("emitter", emitter) 23 | app.use(translationPlugin) 24 | app.use(router) 25 | app.use(store) 26 | 27 | app.use(FrappeUI, { socketio: false }) 28 | const socket = initSocket() 29 | const realtime = new RealTimeHandler(socket) 30 | app.provide("realtime", realtime) 31 | app.config.globalProperties.$realtime = realtime 32 | app.directive("on-outside-click", onOutsideClickDirective) 33 | app.use( 34 | VueTippy, 35 | // optional 36 | { 37 | directive: "tippy", // => v-tippy 38 | component: "tippy", // => 39 | } 40 | ) 41 | app.directive("focus", { 42 | mounted: (el) => el.focus(), 43 | }) 44 | 45 | setConfig("resourceFetcher", (options) => { 46 | return frappeRequest({ 47 | ...options, 48 | onError(err) { 49 | if (err.messages && err.messages[0]) { 50 | return 51 | } 52 | }, 53 | }) 54 | }) 55 | app.component("Button", Button) 56 | app.mount("#app") 57 | -------------------------------------------------------------------------------- /frontend/src/pages/Favourites.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /frontend/src/pages/Personal.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /frontend/src/pages/Recents.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 46 | -------------------------------------------------------------------------------- /frontend/src/pages/Shared.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | -------------------------------------------------------------------------------- /frontend/src/pages/Team.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /frontend/src/pages/Trash.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /frontend/src/translation.js: -------------------------------------------------------------------------------- 1 | import { createResource } from "frappe-ui" 2 | 3 | export default function translationPlugin(app) { 4 | app.config.globalProperties.__ = translate 5 | window.__ = translate 6 | if (!window.translatedMessages) fetchTranslations() 7 | } 8 | 9 | function translate(message) { 10 | let translatedMessages = window.translatedMessages || {} 11 | let translatedMessage = translatedMessages[message] || message 12 | 13 | const hasPlaceholders = /{\d+}/.test(message) 14 | if (!hasPlaceholders) { 15 | return translatedMessage 16 | } 17 | return { 18 | format: function (...args) { 19 | return translatedMessage.replace(/{(\d+)}/g, function (match, number) { 20 | return typeof args[number] != "undefined" ? args[number] : match 21 | }) 22 | }, 23 | } 24 | } 25 | 26 | function fetchTranslations() { 27 | createResource({ 28 | url: "drive.api.product.get_translations", 29 | cache: "translations", 30 | auto: true, 31 | transform: (data) => { 32 | window.translatedMessages = data 33 | }, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/utils/dragSelect.js: -------------------------------------------------------------------------------- 1 | export function calculateRectangle(coordinates) { 2 | const x3 = Math.min(coordinates.x1, coordinates.x2) 3 | const x4 = Math.max(coordinates.x1, coordinates.x2) 4 | const y3 = Math.min(coordinates.y1, coordinates.y2) 5 | const y4 = Math.max(coordinates.y1, coordinates.y2) 6 | return { 7 | left: x3 + "px", 8 | top: y3 + "px", 9 | width: x4 - x3 + "px", 10 | height: y4 - y3 + "px", 11 | } 12 | } 13 | 14 | export function handleDragSelect(entityElements, coordinates) { 15 | const selectedEntities = new Set() 16 | entityElements.forEach((element) => { 17 | const elementRect = element.getBoundingClientRect() 18 | const maxX = Math.max(coordinates.x1, coordinates.x2) 19 | const minX = Math.min(coordinates.x1, coordinates.x2) 20 | const maxY = Math.max(coordinates.y1, coordinates.y2) 21 | const minY = Math.min(coordinates.y1, coordinates.y2) 22 | if ( 23 | ((elementRect.top >= minY && elementRect.top <= maxY) || 24 | (elementRect.bottom >= minY && elementRect.bottom <= maxY) || 25 | (minY >= elementRect.top && minY <= elementRect.bottom)) && 26 | ((elementRect.left >= minX && elementRect.left <= maxX) || 27 | (elementRect.right >= minX && elementRect.right <= maxX) || 28 | (minX >= elementRect.left && minX <= elementRect.right)) 29 | ) { 30 | //element.classList.add("bg-gray-100", "border-gray-300"); 31 | selectedEntities.add(element.id) 32 | } else { 33 | // element.classList.remove("bg-gray-100", "border-gray-300"); 34 | } 35 | }) 36 | return selectedEntities 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/utils/file-to-base64.js: -------------------------------------------------------------------------------- 1 | export default (file) => { 2 | return new Promise((resolve) => { 3 | let reader = new FileReader() 4 | reader.onloadend = () => { 5 | resolve(reader.result) 6 | } 7 | reader.readAsDataURL(file) 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/utils/fuzzySearcher.js: -------------------------------------------------------------------------------- 1 | import fuzzysort from "fuzzysort" 2 | 3 | export default function getFilteredEntities(search, entities) { 4 | return fuzzysort 5 | .go(search, entities, { 6 | limit: 5, 7 | threshold: -100000, 8 | key: "title", 9 | all: true, 10 | }) 11 | .map((result) => result.obj) 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/utils/getIconUrl.js: -------------------------------------------------------------------------------- 1 | export function getIconUrl(file_type) { 2 | return new URL( 3 | `/src/assets/images/icons/${file_type.toLowerCase()}.svg`, 4 | import.meta.url 5 | ) 6 | } 7 | 8 | export function getThumbnailUrl(name, file_type) { 9 | const HTML_THUMBNAILS = ["Markdown", "Code", "Text", "Document"] 10 | const IMAGE_THUMBNAILS = ["Image", "Video", "PDF", "Presentation"] 11 | const is_image = IMAGE_THUMBNAILS.includes(file_type) 12 | const iconURL = getIconUrl(file_type.toLowerCase()) 13 | if (!is_image && !HTML_THUMBNAILS.includes(file_type)) 14 | return [null, iconURL, true] 15 | return [ 16 | `/api/method/drive.api.files.get_thumbnail?entity_name=${name}`, 17 | iconURL, 18 | is_image, 19 | ] 20 | } 21 | 22 | async function get_thumbnail_content(entity_name) { 23 | const fileUrl = `` 24 | 25 | const content = await fetch(fileUrl) 26 | const blob = await content.blob() 27 | if (content.ok && blob.size) { 28 | return blob 29 | } else { 30 | throw new Error(`Request failed with status ${content.status}`) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/utils/getLink.js: -------------------------------------------------------------------------------- 1 | import { toast } from "@/utils/toasts.js" 2 | import router from "../router.js" 3 | 4 | export function getLinkStem(entity) { 5 | return `${ 6 | { 7 | true: "file", 8 | [new Boolean(entity.is_group)]: "folder", 9 | [new Boolean(entity.document)]: "document", 10 | }[true] 11 | }/${entity.name}` 12 | } 13 | 14 | export function getLink(entity, copy = true, withDomain = true) { 15 | const team = router.currentRoute.value.params.team 16 | let link = entity.is_link 17 | ? entity.path 18 | : `${ 19 | withDomain ? window.location.origin + "/drive" : "" 20 | }/t/${team}/${getLinkStem(entity)}` 21 | 22 | if (!copy) return link 23 | try { 24 | copyToClipboard(link).then(() => toast("Copied link")) 25 | } catch (err) { 26 | if (err.name === "NotAllowedError") { 27 | toast({ 28 | icon: "alert-triangle", 29 | iconClasses: "text-red-700", 30 | title: "Clipboard permission denied", 31 | position: "bottom-right", 32 | }) 33 | } else { 34 | console.error("Failed to copy link:", err) 35 | } 36 | } 37 | } 38 | 39 | const copyToClipboard = (str) => { 40 | if (navigator && navigator.clipboard && navigator.clipboard.writeText) { 41 | return navigator.clipboard.writeText(str) 42 | } else { 43 | // Fallback to the legacy clipboard API 44 | const textArea = document.createElement("textarea") 45 | textArea.value = str 46 | document.body.appendChild(textArea) 47 | textArea.select() 48 | document.execCommand("copy") 49 | document.body.removeChild(textArea) 50 | return Promise.resolve() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /frontend/src/utils/markdown.js: -------------------------------------------------------------------------------- 1 | import showdown from "showdown" 2 | 3 | export function markdownToHTML(text) { 4 | const converter = new showdown.Converter() 5 | return converter.makeHtml(text) 6 | } 7 | 8 | export function htmlToMarkdown(text) { 9 | const converter = new showdown.Converter() 10 | return converter.makeMarkdown(text) 11 | } 12 | 13 | export function detectMarkdown(text) { 14 | const lines = text.split("\n") 15 | const markdown = lines.filter( 16 | (line) => 17 | line.startsWith("![") || 18 | line.startsWith("#") || 19 | line.startsWith("> ") || 20 | line.startsWith("*") || 21 | line.startsWith("- ") || 22 | line.startsWith("1. ") || 23 | line.startsWith("```") || 24 | line.startsWith("`") || 25 | line.startsWith("[") || 26 | line.startsWith("---") 27 | ) 28 | return markdown.length > 0 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/utils/random-color.js: -------------------------------------------------------------------------------- 1 | const colors = { 2 | blue: "#0000ff", 3 | cyan: "#00ffff", 4 | darkblue: "#00008b", 5 | darkcyan: "#008b8b", 6 | darkgrey: "#a9a9a9", 7 | darkkhaki: "#bdb76b", 8 | darksalmon: "#e9967a", 9 | darkviolet: "#9400d3", 10 | gold: "#ffd700", 11 | green: "#008000", 12 | lightblue: "#add8e6", 13 | lightgreen: "#90ee90", 14 | magenta: "#ff00ff", 15 | maroon: "#800000", 16 | navy: "#000080", 17 | orange: "#ffa500", 18 | pink: "#ffc0cb", 19 | purple: "#800080", 20 | red: "#ff0000", 21 | yellow: "#ffff00", 22 | } 23 | 24 | const colorArray = Object.values(colors) 25 | export function getRandomColor() { 26 | const randomIndex = Math.floor(Math.random() * colorArray.length) 27 | return colorArray[randomIndex].trim() 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/utils/theme.js: -------------------------------------------------------------------------------- 1 | import resolveConfig from "tailwindcss/resolveConfig" 2 | import tailwindConfig from "tailwind.config.js" 3 | 4 | export const config = resolveConfig(tailwindConfig) 5 | export const theme = config.theme 6 | -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import vue from "@vitejs/plugin-vue" 3 | import path from "path" 4 | import frappeui from "frappe-ui/vite" 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | frappeui({ 10 | frappeProxy: true, // Setup proxy to Frappe backend 11 | lucideIcons: true, // Configure Lucide icons 12 | jinjaBootData: true, // Inject server-side boot data 13 | // Production build config for asset paths and HTML output 14 | buildConfig: { 15 | indexHtmlPath: "../drive/www/drive.html", 16 | }, 17 | }), 18 | vue(), 19 | ], 20 | define: { 21 | "process.env.IS_PREACT": JSON.stringify("true"), 22 | }, 23 | resolve: { 24 | alias: { 25 | "@": path.resolve(__dirname, "src"), 26 | "tailwind.config.js": path.resolve(__dirname, "tailwind.config.js"), 27 | }, 28 | }, 29 | build: { 30 | sourcemap: true, 31 | outDir: `../${path.basename(path.resolve(".."))}/public/frontend`, 32 | emptyOutDir: true, 33 | target: "esnext", 34 | commonjsOptions: { 35 | include: [/tailwind.config.js/, /node_modules/], 36 | }, 37 | }, 38 | server: { 39 | allowedHosts: ["drive.localhost"], 40 | }, 41 | ssr: { 42 | external: { html2canvas: "html2canvas", dompurify: "dompurify" }, 43 | }, 44 | optimizeDeps: { 45 | esbuildOptions: { target: "esnext" }, 46 | include: ["feather-icons", "showdown", "tailwind.config.js", "lowlight"], 47 | }, 48 | }) 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "frontend", 5 | "frappe-ui" 6 | ], 7 | "scripts": { 8 | "postinstall": "cd frontend && yarn install", 9 | "dev": "cd frontend && yarn dev", 10 | "build": "cd frontend && yarn build" 11 | }, 12 | "dependencies": {} 13 | } 14 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "drive" 3 | authors = [ 4 | { name = "Frappe Technologies Pvt Ltd", email = "developers@frappe.io" }, 5 | ] 6 | description = "An easy to use, document sharing and management solution." 7 | requires-python = ">=3.10" 8 | readme = "README.md" 9 | dynamic = ["version"] 10 | dependencies = [ 11 | "Pillow>=10.0.0,<11.0.0", 12 | "opencv-python-headless>=4.10.0.84", 13 | "python-magic==0.4.27", 14 | "mimemapper==0.4.1", 15 | "unoconv>=0.9.0", 16 | "PyJWT>=2.8.0", 17 | "thumbnail>=1.5.0", 18 | "boto3==1.37.31", 19 | ] 20 | 21 | [build-system] 22 | requires = ["flit_core >=3.4,<4"] 23 | build-backend = "flit_core.buildapi" 24 | 25 | [tool.black] 26 | line-length = 99 27 | 28 | [tool.isort] 29 | line_length = 99 30 | multi_line_output = 3 31 | include_trailing_comma = true 32 | force_grid_wrap = 0 33 | use_parentheses = true 34 | ensure_newline_before_comments = true 35 | indent = "\t" 36 | 37 | # press reads this on install 38 | [deploy.dependencies.apt] 39 | packages = [ 40 | "ffmpeg", 41 | ] -------------------------------------------------------------------------------- /realtime/handlers.js: -------------------------------------------------------------------------------- 1 | // same room evals as frappe 2 | const doc_room = (doctype, docname) => "doc:" + doctype + "/" + docname; 3 | const open_doc_room = (doctype, docname) => 4 | "open_doc:" + doctype + "/" + docname; 5 | const doctype_room = (doctype) => "doctype:" + doctype; 6 | const task_room = (task_id) => "task_progress:" + task_id; 7 | const user_room = (user) => "user:" + user; 8 | 9 | let drive_handlers = (socket) => { 10 | socket.on( 11 | "document_version_change_emit", 12 | (doctype, document, user, user_image, clientID) => { 13 | let room = doc_room(doctype, document); 14 | socket.nsp.to(room).emit("document_version_change_recv", { 15 | doctype: doctype, 16 | docname: document, 17 | author: user, 18 | author_image: user_image, 19 | author_id: clientID, 20 | }); 21 | } 22 | ); 23 | }; 24 | 25 | module.exports = drive_handlers; 26 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | black 2 | Pillow 3 | opencv-python 4 | mimemapper 5 | unoconv 6 | thumbnail 7 | boto3 8 | PyJWT -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("requirements.txt") as f: 4 | install_requires = f.read().strip().split("\n") 5 | 6 | # get version from __version__ variable in drive/__init__.py 7 | from drive import __version__ as version 8 | 9 | setup( 10 | name="drive", 11 | version=version, 12 | description="An easy to use, document sharing and management solution.", 13 | author="Frappe Technologies Pvt. Ltd.", 14 | author_email="hello@frappe.io", 15 | packages=find_packages(), 16 | zip_safe=False, 17 | include_package_data=True, 18 | install_requires=install_requires 19 | ) 20 | --------------------------------------------------------------------------------