├── .env ├── .gitignore ├── Backend ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── .vscode │ └── launch.json ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── ariseontech │ │ └── joindesk │ │ ├── AuditModel.java │ │ ├── DiffMatchPatch.java │ │ ├── HelperUtil.java │ │ ├── JoindeskApplication.java │ │ ├── SetupInitializer.java │ │ ├── SystemInfo.java │ │ ├── WebSecurityConfig.java │ │ ├── WebSocketConfig.java │ │ ├── auth │ │ ├── domain │ │ │ ├── Authority.java │ │ │ ├── AuthorityCode.java │ │ │ ├── AuthorityGlobal.java │ │ │ ├── AuthorityProject.java │ │ │ ├── ChangePasswordDTO.java │ │ │ ├── ForgotPasswordRequestDTO.java │ │ │ ├── IPWhiteBlacklist.java │ │ │ ├── Login.java │ │ │ ├── LoginRequestDTO.java │ │ │ ├── PasswordResetToken.java │ │ │ ├── PreferredAuthTypes.java │ │ │ ├── SlackUser.java │ │ │ └── Token.java │ │ ├── filter │ │ │ ├── CorsFilter.java │ │ │ └── TokenAuthenticationFilter.java │ │ ├── repo │ │ │ ├── AuthorityGlobalRepo.java │ │ │ ├── AuthorityProjectRepo.java │ │ │ ├── IPWhiteBlackListRepo.java │ │ │ ├── LoginRepo.java │ │ │ ├── PasswordResetTokenRepo.java │ │ │ └── TokenRepo.java │ │ ├── service │ │ │ ├── AuthService.java │ │ │ ├── IPWhiteBlackListingService.java │ │ │ ├── JwtTokenUtil.java │ │ │ ├── LoginDetail.java │ │ │ └── UserService.java │ │ ├── util │ │ │ ├── ApiUtil.java │ │ │ ├── AuditorAwareImpl.java │ │ │ ├── CurrentLogin.java │ │ │ └── View.java │ │ └── web │ │ │ ├── AuthController.java │ │ │ ├── IPRangeController.java │ │ │ ├── MediaController.java │ │ │ ├── MyAccountController.java │ │ │ └── UserController.java │ │ ├── board │ │ ├── domain │ │ │ ├── Board.java │ │ │ └── Lane.java │ │ ├── repo │ │ │ ├── BoardRepo.java │ │ │ └── LaneRepo.java │ │ ├── service │ │ │ └── BoardService.java │ │ └── web │ │ │ └── BoardController.java │ │ ├── event │ │ ├── domain │ │ │ ├── IssueEvent.java │ │ │ ├── IssueEventHandler.java │ │ │ ├── IssueEventType.java │ │ │ ├── JDEvent.java │ │ │ ├── JDEventHandler.java │ │ │ ├── JDEventType.java │ │ │ └── Subscription.java │ │ ├── repo │ │ │ ├── IssueEventRepo.java │ │ │ ├── JDEventRepo.java │ │ │ └── SubscriptionRepo.java │ │ ├── service │ │ │ └── EmailService.java │ │ └── web │ │ │ ├── SlackEventController.java │ │ │ └── SubscriptionController.java │ │ ├── exception │ │ ├── CustomExceptionHandler.java │ │ ├── ErrorCode.java │ │ ├── ErrorDetails.java │ │ ├── JDException.java │ │ └── JDTransitionException.java │ │ ├── git │ │ ├── domain │ │ │ ├── GitBranch.java │ │ │ ├── GitCommit.java │ │ │ ├── GitHook.java │ │ │ ├── GitRepoType.java │ │ │ └── Repository.java │ │ ├── repo │ │ │ ├── GitBranchRepo.java │ │ │ ├── GitCommitRepo.java │ │ │ ├── HookRepo.java │ │ │ └── RepositoryRepo.java │ │ ├── service │ │ │ ├── GitHookService.java │ │ │ └── GitRepositoryService.java │ │ └── web │ │ │ ├── GitHookController.java │ │ │ └── GitRepositoryController.java │ │ ├── issues │ │ ├── domain │ │ │ ├── Attachment.java │ │ │ ├── Comment.java │ │ │ ├── FilteredIssueDTO.java │ │ │ ├── Issue.java │ │ │ ├── IssueCustomField.java │ │ │ ├── IssueFilter.java │ │ │ ├── IssueFilterDTO.java │ │ │ ├── IssueFilterGroup.java │ │ │ ├── IssueFilterOptions.java │ │ │ ├── IssueHistory.java │ │ │ ├── IssueLifeCycle.java │ │ │ ├── IssueMentions.java │ │ │ ├── IssueOtherRelationship.java │ │ │ ├── IssueRelationship.java │ │ │ ├── IssueSearchDTO.java │ │ │ ├── IssueSearchQuery.java │ │ │ ├── IssueSearchQueryRule.java │ │ │ ├── IssueStatus.java │ │ │ ├── IssueType.java │ │ │ ├── IssueView.java │ │ │ ├── IssuesFilterDTO.java │ │ │ ├── Label.java │ │ │ ├── Priority.java │ │ │ ├── QuickUpdate.java │ │ │ ├── Relationship.java │ │ │ ├── Resolution.java │ │ │ ├── Task.java │ │ │ ├── Version.java │ │ │ ├── Watchers.java │ │ │ ├── WorkLog.java │ │ │ ├── Workflow.java │ │ │ ├── WorkflowChange.java │ │ │ ├── WorkflowStep.java │ │ │ ├── WorkflowTransition.java │ │ │ ├── WorkflowTransitionProperties.java │ │ │ ├── WorkflowTransitionPropertySubTypes.java │ │ │ └── WorkflowTransitionPropertyTypes.java │ │ ├── repo │ │ │ ├── AttachmentRepo.java │ │ │ ├── CommentRepo.java │ │ │ ├── IssueCustomRepo.java │ │ │ ├── IssueFilterRepo.java │ │ │ ├── IssueFilteringRepo.java │ │ │ ├── IssueHistoryRepo.java │ │ │ ├── IssueMentionsRepo.java │ │ │ ├── IssueOtherRelationshipRepo.java │ │ │ ├── IssueRelationshipRepo.java │ │ │ ├── IssueRepo.java │ │ │ ├── IssueSearchCustomRepo.java │ │ │ ├── IssueStatusRepo.java │ │ │ ├── IssueTypeRepo.java │ │ │ ├── IssueViewRepo.java │ │ │ ├── LabelRepo.java │ │ │ ├── RelationshipRepo.java │ │ │ ├── ReportDTO.java │ │ │ ├── ReportQueryInterfaceDTO.java │ │ │ ├── ResolutionRepo.java │ │ │ ├── TaskRepo.java │ │ │ ├── VersionRepo.java │ │ │ ├── WatchersRepo.java │ │ │ ├── WorkLogRepo.java │ │ │ ├── WorkflowRepo.java │ │ │ ├── WorkflowStepRepo.java │ │ │ ├── WorkflowStepTransitionDTO.java │ │ │ ├── WorkflowTransitionPropertyRepo.java │ │ │ └── WorkflowTransitionRepo.java │ │ ├── service │ │ │ ├── ExcelExportView.java │ │ │ ├── IssueAsyncService.java │ │ │ ├── IssueService.java │ │ │ ├── IssueTypeService.java │ │ │ ├── RelationshipService.java │ │ │ ├── ReportService.java │ │ │ ├── ResolutionService.java │ │ │ ├── VersionService.java │ │ │ ├── WorkLogService.java │ │ │ └── WorkflowService.java │ │ └── web │ │ │ ├── IssueController.java │ │ │ ├── IssueTypeController.java │ │ │ ├── ManageController.java │ │ │ ├── ReportController.java │ │ │ ├── WorkLogController.java │ │ │ └── WorkflowController.java │ │ ├── page │ │ ├── domain │ │ │ ├── Page.java │ │ │ ├── PageAttachment.java │ │ │ ├── PageData.java │ │ │ ├── PagePath.java │ │ │ └── PageRevision.java │ │ ├── repo │ │ │ ├── PageAttachmentRepo.java │ │ │ ├── PageCustomRepo.java │ │ │ ├── PageRepo.java │ │ │ └── PageRevisionRepo.java │ │ ├── service │ │ │ └── PageService.java │ │ └── web │ │ │ └── PageController.java │ │ ├── project │ │ ├── domain │ │ │ ├── Component.java │ │ │ ├── CustomField.java │ │ │ ├── CustomFieldType.java │ │ │ ├── GlobalGroup.java │ │ │ ├── Group.java │ │ │ ├── JDConfiguration.java │ │ │ ├── Project.java │ │ │ ├── SlackChannel.java │ │ │ └── TimeTracking.java │ │ ├── repo │ │ │ ├── ComponentRepo.java │ │ │ ├── CustomFieldRepo.java │ │ │ ├── GlobalGroupRepo.java │ │ │ ├── GroupRepo.java │ │ │ ├── JDConfigurationRepo.java │ │ │ ├── ProjectRepo.java │ │ │ └── TimeTrackingRepo.java │ │ ├── service │ │ │ ├── ComponentService.java │ │ │ ├── ConfigurationService.java │ │ │ ├── CustomFieldService.java │ │ │ ├── GroupService.java │ │ │ ├── LabelService.java │ │ │ └── ProjectService.java │ │ └── web │ │ │ ├── AppConfigController.java │ │ │ ├── ComponentController.java │ │ │ ├── CustomFieldController.java │ │ │ ├── GroupController.java │ │ │ └── ProjectController.java │ │ ├── scheduler │ │ ├── JDJobRefresher.java │ │ ├── JDScheduledJobRunner.java │ │ ├── domain │ │ │ └── JDScheduler.java │ │ ├── repo │ │ │ └── JDSchedulerRepo.java │ │ └── service │ │ │ └── JDSchedulerService.java │ │ ├── slack │ │ ├── SlackService.java │ │ └── web │ │ │ └── SlackController.java │ │ ├── webhook │ │ ├── domain │ │ │ ├── WebHook.java │ │ │ ├── WebHookHeaders.java │ │ │ ├── WebHookLog.java │ │ │ ├── WebHookLogHeaders.java │ │ │ └── WebHookRequestType.java │ │ ├── repo │ │ │ ├── WebHookLogRepo.java │ │ │ └── WebHookRepo.java │ │ ├── service │ │ │ └── WebHookService.java │ │ └── web │ │ │ └── WebHookController.java │ │ └── wiki │ │ ├── domain │ │ ├── Wiki.java │ │ ├── WikiAttachment.java │ │ ├── WikiData.java │ │ ├── WikiFolder.java │ │ ├── WikiPath.java │ │ └── WikiRevision.java │ │ ├── repo │ │ ├── WikiAttachmentRepo.java │ │ ├── WikiCustomRepo.java │ │ ├── WikiFolderRepo.java │ │ ├── WikiRepo.java │ │ └── WikiRevisionRepo.java │ │ ├── service │ │ └── WikiService.java │ │ └── web │ │ └── WikiController.java │ └── resources │ ├── application-dev.properties │ ├── application-docker.properties │ ├── application.properties │ ├── messages.properties │ └── squiggly.properties ├── Frontend ├── .editorconfig ├── .gitignore ├── Dockerfile ├── README.md ├── angular.json ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── jd.cert ├── jd.key ├── karma.conf.js ├── nginx.conf ├── ngsw-config.json ├── package-lock.json ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── admin.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app │ │ │ ├── admin │ │ │ │ ├── access │ │ │ │ │ ├── access.component.css │ │ │ │ │ ├── access.component.html │ │ │ │ │ └── access.component.ts │ │ │ │ ├── adminhome │ │ │ │ │ ├── adminhome.component.css │ │ │ │ │ ├── adminhome.component.html │ │ │ │ │ └── adminhome.component.ts │ │ │ │ ├── config │ │ │ │ │ ├── config.component.css │ │ │ │ │ ├── config.component.html │ │ │ │ │ └── config.component.ts │ │ │ │ ├── global-group │ │ │ │ │ ├── global-group.component.css │ │ │ │ │ ├── global-group.component.html │ │ │ │ │ └── global-group.component.ts │ │ │ │ ├── link-type │ │ │ │ │ ├── link-type.component.css │ │ │ │ │ ├── link-type.component.html │ │ │ │ │ └── link-type.component.ts │ │ │ │ ├── manage.service.ts │ │ │ │ ├── project │ │ │ │ │ ├── project-view │ │ │ │ │ │ ├── project-view.component.css │ │ │ │ │ │ ├── project-view.component.html │ │ │ │ │ │ └── project-view.component.ts │ │ │ │ │ ├── project.component.css │ │ │ │ │ ├── project.component.html │ │ │ │ │ └── project.component.ts │ │ │ │ ├── resolution │ │ │ │ │ ├── resolution.component.css │ │ │ │ │ ├── resolution.component.html │ │ │ │ │ └── resolution.component.ts │ │ │ │ ├── user │ │ │ │ │ ├── user.component.css │ │ │ │ │ ├── user.component.html │ │ │ │ │ ├── user.component.ts │ │ │ │ │ ├── user.service.ts │ │ │ │ │ └── user.ts │ │ │ │ └── webhook │ │ │ │ │ ├── webhook.component.css │ │ │ │ │ ├── webhook.component.html │ │ │ │ │ └── webhook.component.ts │ │ │ ├── calendar-worklog │ │ │ │ ├── calendar-worklog.component.css │ │ │ │ ├── calendar-worklog.component.html │ │ │ │ └── calendar-worklog.component.ts │ │ │ ├── calendar │ │ │ │ ├── calendar.component.css │ │ │ │ ├── calendar.component.html │ │ │ │ └── calendar.component.ts │ │ │ ├── dashboard │ │ │ │ ├── board │ │ │ │ │ ├── board-overview │ │ │ │ │ │ ├── board-overview.component.css │ │ │ │ │ │ ├── board-overview.component.html │ │ │ │ │ │ └── board-overview.component.ts │ │ │ │ │ ├── board-view │ │ │ │ │ │ ├── board-view.component.css │ │ │ │ │ │ ├── board-view.component.html │ │ │ │ │ │ └── board-view.component.ts │ │ │ │ │ ├── board.service.ts │ │ │ │ │ ├── board.ts │ │ │ │ │ └── lane-edit │ │ │ │ │ │ ├── lane-edit.component.css │ │ │ │ │ │ ├── lane-edit.component.html │ │ │ │ │ │ └── lane-edit.component.ts │ │ │ │ ├── dashboard-home │ │ │ │ │ ├── dashboard-home.component.css │ │ │ │ │ ├── dashboard-home.component.html │ │ │ │ │ └── dashboard-home.component.ts │ │ │ │ ├── dashboard.component.css │ │ │ │ ├── dashboard.component.html │ │ │ │ ├── dashboard.component.ts │ │ │ │ ├── dashboard.service.ts │ │ │ │ ├── list │ │ │ │ │ ├── issue-list.component.html │ │ │ │ │ └── issue-list.component.ts │ │ │ │ └── ticket-view │ │ │ │ │ ├── ticket-view.component.css │ │ │ │ │ ├── ticket-view.component.html │ │ │ │ │ └── ticket-view.component.ts │ │ │ ├── gantt │ │ │ │ ├── gantt.component.css │ │ │ │ ├── gantt.component.html │ │ │ │ └── gantt.component.ts │ │ │ ├── home │ │ │ │ ├── home.component.css │ │ │ │ ├── home.component.html │ │ │ │ └── home.component.ts │ │ │ ├── http.service.ts │ │ │ ├── issue │ │ │ │ ├── create │ │ │ │ │ ├── create.component.css │ │ │ │ │ ├── create.component.html │ │ │ │ │ └── create.component.ts │ │ │ │ ├── customfield-inline │ │ │ │ │ ├── customfield-inline.component.css │ │ │ │ │ ├── customfield-inline.component.html │ │ │ │ │ └── customfield-inline.component.ts │ │ │ │ ├── inline-edit │ │ │ │ │ ├── inline-edit.component.css │ │ │ │ │ ├── inline-edit.component.html │ │ │ │ │ └── inline-edit.component.ts │ │ │ │ ├── issue-modal.service.ts │ │ │ │ ├── issue-transition-dialog │ │ │ │ │ ├── issue-transition-dialog.component.css │ │ │ │ │ ├── issue-transition-dialog.component.html │ │ │ │ │ └── issue-transition-dialog.component.ts │ │ │ │ ├── issue-view │ │ │ │ │ ├── issue-view.component.css │ │ │ │ │ ├── issue-view.component.html │ │ │ │ │ └── issue-view.component.ts │ │ │ │ ├── issue.service.ts │ │ │ │ ├── issue.ts │ │ │ │ ├── jd-editor │ │ │ │ │ ├── jd-editor.component.css │ │ │ │ │ ├── jd-editor.component.html │ │ │ │ │ └── jd-editor.component.ts │ │ │ │ ├── ticket-dialog │ │ │ │ │ ├── ticket-dialog.component.css │ │ │ │ │ ├── ticket-dialog.component.html │ │ │ │ │ └── ticket-dialog.component.ts │ │ │ │ └── worklog-dialog │ │ │ │ │ ├── worklog-dialog.component.css │ │ │ │ │ ├── worklog-dialog.component.html │ │ │ │ │ └── worklog-dialog.component.ts │ │ │ ├── myaccount │ │ │ │ ├── myaccount.component.css │ │ │ │ ├── myaccount.component.html │ │ │ │ ├── myaccount.component.ts │ │ │ │ ├── myaccountservice.service.ts │ │ │ │ └── usermedia │ │ │ │ │ ├── usermedia.component.css │ │ │ │ │ ├── usermedia.component.html │ │ │ │ │ └── usermedia.component.ts │ │ │ ├── pages │ │ │ │ ├── page-edit │ │ │ │ │ ├── page-edit.component.css │ │ │ │ │ ├── page-edit.component.html │ │ │ │ │ └── page-edit.component.ts │ │ │ │ ├── page-history │ │ │ │ │ ├── page-history.component.css │ │ │ │ │ ├── page-history.component.html │ │ │ │ │ └── page-history.component.ts │ │ │ │ ├── page-layout │ │ │ │ │ ├── page-layout.component.css │ │ │ │ │ ├── page-layout.component.html │ │ │ │ │ └── page-layout.component.ts │ │ │ │ ├── page-view │ │ │ │ │ ├── page-view.component.css │ │ │ │ │ ├── page-view.component.html │ │ │ │ │ └── page-view.component.ts │ │ │ │ ├── page.ts │ │ │ │ └── pages.service.ts │ │ │ ├── project-manage │ │ │ │ ├── access.ts │ │ │ │ ├── custom-field │ │ │ │ │ ├── custom-field.service.ts │ │ │ │ │ ├── custom-field.ts │ │ │ │ │ ├── custom-fields.component.css │ │ │ │ │ ├── custom-fields.component.html │ │ │ │ │ └── custom-fields.component.ts │ │ │ │ ├── group.service.ts │ │ │ │ ├── group.ts │ │ │ │ ├── issue-type.service.ts │ │ │ │ ├── issue-type.ts │ │ │ │ ├── jdcomponent │ │ │ │ │ ├── jdcomponent.component.css │ │ │ │ │ ├── jdcomponent.component.html │ │ │ │ │ ├── jdcomponent.component.ts │ │ │ │ │ ├── jdcomponent.service.ts │ │ │ │ │ └── jdcomponent.ts │ │ │ │ ├── project-manage-detail │ │ │ │ │ ├── project-manage-detail.component.css │ │ │ │ │ ├── project-manage-detail.component.html │ │ │ │ │ └── project-manage-detail.component.ts │ │ │ │ ├── project-manage-group │ │ │ │ │ ├── project-manage-group.component.css │ │ │ │ │ ├── project-manage-group.component.html │ │ │ │ │ └── project-manage-group.component.ts │ │ │ │ ├── project-manage-issue-types │ │ │ │ │ ├── project-manage-issue-types.component.css │ │ │ │ │ ├── project-manage-issue-types.component.html │ │ │ │ │ └── project-manage-issue-types.component.ts │ │ │ │ ├── project-manage-overview │ │ │ │ │ ├── project-manage-overview.component.css │ │ │ │ │ ├── project-manage-overview.component.html │ │ │ │ │ └── project-manage-overview.component.ts │ │ │ │ ├── project-manage-repository │ │ │ │ │ ├── project-manage-repository.component.css │ │ │ │ │ ├── project-manage-repository.component.html │ │ │ │ │ └── project-manage-repository.component.ts │ │ │ │ ├── project-manage-timetracking │ │ │ │ │ ├── project-manage-timetracking.component.css │ │ │ │ │ ├── project-manage-timetracking.component.html │ │ │ │ │ └── project-manage-timetracking.component.ts │ │ │ │ ├── project-manage-version │ │ │ │ │ ├── project-manage-version.component.css │ │ │ │ │ ├── project-manage-version.component.html │ │ │ │ │ └── project-manage-version.component.ts │ │ │ │ ├── project-manage-workflow │ │ │ │ │ ├── project-manage-workflow.component.css │ │ │ │ │ ├── project-manage-workflow.component.html │ │ │ │ │ ├── project-manage-workflow.component.ts │ │ │ │ │ ├── transition-edit │ │ │ │ │ │ ├── transition-edit.component.css │ │ │ │ │ │ ├── transition-edit.component.html │ │ │ │ │ │ └── transition-edit.component.ts │ │ │ │ │ ├── workflow-edit │ │ │ │ │ │ ├── workflow-edit.component.css │ │ │ │ │ │ ├── workflow-edit.component.html │ │ │ │ │ │ └── workflow-edit.component.ts │ │ │ │ │ ├── workflow-preview │ │ │ │ │ │ ├── workflow-preview.component.css │ │ │ │ │ │ ├── workflow-preview.component.html │ │ │ │ │ │ └── workflow-preview.component.ts │ │ │ │ │ └── workflow-view │ │ │ │ │ │ ├── workflow-view.component.css │ │ │ │ │ │ ├── workflow-view.component.html │ │ │ │ │ │ └── workflow-view.component.ts │ │ │ │ ├── project-manage.component.css │ │ │ │ ├── project-manage.component.html │ │ │ │ ├── project-manage.component.ts │ │ │ │ ├── project.service.ts │ │ │ │ ├── project.ts │ │ │ │ ├── webhook-headers.ts │ │ │ │ ├── webhook.ts │ │ │ │ ├── workflow-step.ts │ │ │ │ ├── workflow-transition-properties.ts │ │ │ │ ├── workflow-transition.ts │ │ │ │ ├── workflow.service.ts │ │ │ │ └── workflow.ts │ │ │ ├── project-report │ │ │ │ ├── project-report.component.css │ │ │ │ ├── project-report.component.html │ │ │ │ ├── project-report.component.ts │ │ │ │ └── report.service.ts │ │ │ ├── project │ │ │ │ └── release │ │ │ │ │ ├── release.component.css │ │ │ │ │ ├── release.component.html │ │ │ │ │ └── release.component.ts │ │ │ ├── toastr.service.ts │ │ │ └── worklog-dialog │ │ │ │ ├── worklog-dialog.component.css │ │ │ │ ├── worklog-dialog.component.html │ │ │ │ └── worklog-dialog.component.ts │ │ ├── auth │ │ │ ├── adminauth.guard..ts │ │ │ ├── auth.guard.ts │ │ │ ├── authentication.service.ts │ │ │ ├── error.interceptor.ts │ │ │ ├── forgot │ │ │ │ ├── forgot.component.css │ │ │ │ ├── forgot.component.html │ │ │ │ └── forgot.component.ts │ │ │ ├── login │ │ │ │ ├── login.component.css │ │ │ │ ├── login.component.html │ │ │ │ └── login.component.ts │ │ │ ├── register │ │ │ │ ├── register.component.css │ │ │ │ ├── register.component.html │ │ │ │ └── register.component.ts │ │ │ ├── setup │ │ │ │ ├── setup.component.css │ │ │ │ ├── setup.component.html │ │ │ │ └── setup.component.ts │ │ │ └── token.interceptor.ts │ │ ├── general │ │ │ ├── body │ │ │ │ ├── animations.ts │ │ │ │ ├── body.component.css │ │ │ │ ├── body.component.html │ │ │ │ └── body.component.ts │ │ │ ├── content-view.directive.ts │ │ │ ├── footer │ │ │ │ ├── footer.component.css │ │ │ │ ├── footer.component.html │ │ │ │ └── footer.component.ts │ │ │ ├── header │ │ │ │ ├── header.component.css │ │ │ │ ├── header.component.html │ │ │ │ └── header.component.ts │ │ │ ├── icon │ │ │ │ ├── icon.component.html │ │ │ │ └── icon.component.ts │ │ │ ├── layout │ │ │ │ ├── layout.component.css │ │ │ │ ├── layout.component.html │ │ │ │ └── layout.component.ts │ │ │ ├── sidebar │ │ │ │ ├── sidebar.component.css │ │ │ │ ├── sidebar.component.html │ │ │ │ └── sidebar.component.ts │ │ │ └── userFilter.pipe.ts │ │ ├── project-manage.module.ts │ │ ├── project.module.ts │ │ └── shared.module.ts │ ├── assets │ │ ├── css │ │ │ ├── dataTables.bootstrap4.min.css │ │ │ ├── style.css │ │ │ ├── style.min.css │ │ │ └── theme.css │ │ ├── editormd │ │ │ ├── css │ │ │ │ ├── editormd.css │ │ │ │ ├── editormd.logo.css │ │ │ │ ├── editormd.logo.min.css │ │ │ │ ├── editormd.min.css │ │ │ │ ├── editormd.preview.css │ │ │ │ └── editormd.preview.min.css │ │ │ ├── editormd.js │ │ │ ├── editormd.min.js │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── editormd-logo.eot │ │ │ │ ├── editormd-logo.svg │ │ │ │ ├── editormd-logo.ttf │ │ │ │ ├── editormd-logo.woff │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── images │ │ │ │ ├── loading.gif │ │ │ │ ├── loading@2x.gif │ │ │ │ ├── loading@3x.gif │ │ │ │ └── logos │ │ │ │ │ ├── editormd-favicon-16x16.ico │ │ │ │ │ ├── editormd-favicon-24x24.ico │ │ │ │ │ ├── editormd-favicon-32x32.ico │ │ │ │ │ ├── editormd-favicon-48x48.ico │ │ │ │ │ ├── editormd-favicon-64x64.ico │ │ │ │ │ ├── editormd-logo-114x114.png │ │ │ │ │ ├── editormd-logo-120x120.png │ │ │ │ │ ├── editormd-logo-144x144.png │ │ │ │ │ ├── editormd-logo-16x16.png │ │ │ │ │ ├── editormd-logo-180x180.png │ │ │ │ │ ├── editormd-logo-240x240.png │ │ │ │ │ ├── editormd-logo-24x24.png │ │ │ │ │ ├── editormd-logo-320x320.png │ │ │ │ │ ├── editormd-logo-32x32.png │ │ │ │ │ ├── editormd-logo-48x48.png │ │ │ │ │ ├── editormd-logo-57x57.png │ │ │ │ │ ├── editormd-logo-64x64.png │ │ │ │ │ ├── editormd-logo-72x72.png │ │ │ │ │ ├── editormd-logo-96x96.png │ │ │ │ │ └── vi.png │ │ │ ├── languages │ │ │ │ ├── en.js │ │ │ │ └── zh-tw.js │ │ │ ├── lib │ │ │ │ ├── codemirror │ │ │ │ │ ├── AUTHORS │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── addon │ │ │ │ │ │ ├── comment │ │ │ │ │ │ │ ├── comment.js │ │ │ │ │ │ │ └── continuecomment.js │ │ │ │ │ │ ├── dialog │ │ │ │ │ │ │ ├── dialog.css │ │ │ │ │ │ │ └── dialog.js │ │ │ │ │ │ ├── display │ │ │ │ │ │ │ ├── fullscreen.css │ │ │ │ │ │ │ ├── fullscreen.js │ │ │ │ │ │ │ ├── panel.js │ │ │ │ │ │ │ ├── placeholder.js │ │ │ │ │ │ │ └── rulers.js │ │ │ │ │ │ ├── edit │ │ │ │ │ │ │ ├── closebrackets.js │ │ │ │ │ │ │ ├── closetag.js │ │ │ │ │ │ │ ├── continuelist.js │ │ │ │ │ │ │ ├── matchbrackets.js │ │ │ │ │ │ │ ├── matchtags.js │ │ │ │ │ │ │ └── trailingspace.js │ │ │ │ │ │ ├── fold │ │ │ │ │ │ │ ├── brace-fold.js │ │ │ │ │ │ │ ├── comment-fold.js │ │ │ │ │ │ │ ├── foldcode.js │ │ │ │ │ │ │ ├── foldgutter.css │ │ │ │ │ │ │ ├── foldgutter.js │ │ │ │ │ │ │ ├── indent-fold.js │ │ │ │ │ │ │ ├── markdown-fold.js │ │ │ │ │ │ │ └── xml-fold.js │ │ │ │ │ │ ├── hint │ │ │ │ │ │ │ ├── anyword-hint.js │ │ │ │ │ │ │ ├── css-hint.js │ │ │ │ │ │ │ ├── html-hint.js │ │ │ │ │ │ │ ├── javascript-hint.js │ │ │ │ │ │ │ ├── show-hint.css │ │ │ │ │ │ │ ├── show-hint.js │ │ │ │ │ │ │ ├── sql-hint.js │ │ │ │ │ │ │ └── xml-hint.js │ │ │ │ │ │ ├── lint │ │ │ │ │ │ │ ├── coffeescript-lint.js │ │ │ │ │ │ │ ├── css-lint.js │ │ │ │ │ │ │ ├── javascript-lint.js │ │ │ │ │ │ │ ├── json-lint.js │ │ │ │ │ │ │ ├── lint.css │ │ │ │ │ │ │ ├── lint.js │ │ │ │ │ │ │ └── yaml-lint.js │ │ │ │ │ │ ├── merge │ │ │ │ │ │ │ ├── merge.css │ │ │ │ │ │ │ └── merge.js │ │ │ │ │ │ ├── mode │ │ │ │ │ │ │ ├── loadmode.js │ │ │ │ │ │ │ ├── multiplex.js │ │ │ │ │ │ │ ├── multiplex_test.js │ │ │ │ │ │ │ ├── overlay.js │ │ │ │ │ │ │ └── simple.js │ │ │ │ │ │ ├── runmode │ │ │ │ │ │ │ ├── colorize.js │ │ │ │ │ │ │ ├── runmode-standalone.js │ │ │ │ │ │ │ ├── runmode.js │ │ │ │ │ │ │ └── runmode.node.js │ │ │ │ │ │ ├── scroll │ │ │ │ │ │ │ ├── annotatescrollbar.js │ │ │ │ │ │ │ ├── scrollpastend.js │ │ │ │ │ │ │ ├── simplescrollbars.css │ │ │ │ │ │ │ └── simplescrollbars.js │ │ │ │ │ │ ├── search │ │ │ │ │ │ │ ├── match-highlighter.js │ │ │ │ │ │ │ ├── matchesonscrollbar.css │ │ │ │ │ │ │ ├── matchesonscrollbar.js │ │ │ │ │ │ │ ├── search.js │ │ │ │ │ │ │ └── searchcursor.js │ │ │ │ │ │ ├── selection │ │ │ │ │ │ │ ├── active-line.js │ │ │ │ │ │ │ ├── mark-selection.js │ │ │ │ │ │ │ └── selection-pointer.js │ │ │ │ │ │ ├── tern │ │ │ │ │ │ │ ├── tern.css │ │ │ │ │ │ │ ├── tern.js │ │ │ │ │ │ │ └── worker.js │ │ │ │ │ │ └── wrap │ │ │ │ │ │ │ └── hardwrap.js │ │ │ │ │ ├── addons.min.js │ │ │ │ │ ├── bower.json │ │ │ │ │ ├── codemirror.min.css │ │ │ │ │ ├── codemirror.min.js │ │ │ │ │ ├── lib │ │ │ │ │ │ ├── codemirror.css │ │ │ │ │ │ └── codemirror.js │ │ │ │ │ ├── mode │ │ │ │ │ │ ├── apl │ │ │ │ │ │ │ ├── apl.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── asterisk │ │ │ │ │ │ │ ├── asterisk.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── clike │ │ │ │ │ │ │ ├── clike.js │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── scala.html │ │ │ │ │ │ ├── clojure │ │ │ │ │ │ │ ├── clojure.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── cobol │ │ │ │ │ │ │ ├── cobol.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── coffeescript │ │ │ │ │ │ │ ├── coffeescript.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── commonlisp │ │ │ │ │ │ │ ├── commonlisp.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── css │ │ │ │ │ │ │ ├── css.js │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── less.html │ │ │ │ │ │ │ ├── less_test.js │ │ │ │ │ │ │ ├── scss.html │ │ │ │ │ │ │ ├── scss_test.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── cypher │ │ │ │ │ │ │ ├── cypher.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── d │ │ │ │ │ │ │ ├── d.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── dart │ │ │ │ │ │ │ ├── dart.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── diff │ │ │ │ │ │ │ ├── diff.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── django │ │ │ │ │ │ │ ├── django.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── dockerfile │ │ │ │ │ │ │ ├── dockerfile.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── dtd │ │ │ │ │ │ │ ├── dtd.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── dylan │ │ │ │ │ │ │ ├── dylan.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── ebnf │ │ │ │ │ │ │ ├── ebnf.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── ecl │ │ │ │ │ │ │ ├── ecl.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── eiffel │ │ │ │ │ │ │ ├── eiffel.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── erlang │ │ │ │ │ │ │ ├── erlang.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── forth │ │ │ │ │ │ │ ├── forth.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── fortran │ │ │ │ │ │ │ ├── fortran.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── gas │ │ │ │ │ │ │ ├── gas.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── gfm │ │ │ │ │ │ │ ├── gfm.js │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── gherkin │ │ │ │ │ │ │ ├── gherkin.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── go │ │ │ │ │ │ │ ├── go.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── groovy │ │ │ │ │ │ │ ├── groovy.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── haml │ │ │ │ │ │ │ ├── haml.js │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── haskell │ │ │ │ │ │ │ ├── haskell.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── haxe │ │ │ │ │ │ │ ├── haxe.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── htmlembedded │ │ │ │ │ │ │ ├── htmlembedded.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── htmlmixed │ │ │ │ │ │ │ ├── htmlmixed.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── http │ │ │ │ │ │ │ ├── http.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── idl │ │ │ │ │ │ │ ├── idl.js │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── jade │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── jade.js │ │ │ │ │ │ ├── javascript │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── javascript.js │ │ │ │ │ │ │ ├── json-ld.html │ │ │ │ │ │ │ ├── test.js │ │ │ │ │ │ │ └── typescript.html │ │ │ │ │ │ ├── jinja2 │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── jinja2.js │ │ │ │ │ │ ├── julia │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── julia.js │ │ │ │ │ │ ├── kotlin │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── kotlin.js │ │ │ │ │ │ ├── livescript │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── livescript.js │ │ │ │ │ │ ├── lua │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── lua.js │ │ │ │ │ │ ├── markdown │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── markdown.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── meta.js │ │ │ │ │ │ ├── mirc │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── mirc.js │ │ │ │ │ │ ├── mllike │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── mllike.js │ │ │ │ │ │ ├── modelica │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── modelica.js │ │ │ │ │ │ ├── nginx │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── nginx.js │ │ │ │ │ │ ├── ntriples │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── ntriples.js │ │ │ │ │ │ ├── octave │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── octave.js │ │ │ │ │ │ ├── pascal │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── pascal.js │ │ │ │ │ │ ├── pegjs │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── pegjs.js │ │ │ │ │ │ ├── perl │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── perl.js │ │ │ │ │ │ ├── php │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── php.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── pig │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── pig.js │ │ │ │ │ │ ├── properties │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── properties.js │ │ │ │ │ │ ├── puppet │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── puppet.js │ │ │ │ │ │ ├── python │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── python.js │ │ │ │ │ │ ├── q │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── q.js │ │ │ │ │ │ ├── r │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── r.js │ │ │ │ │ │ ├── rpm │ │ │ │ │ │ │ ├── changes │ │ │ │ │ │ │ │ └── index.html │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── rpm.js │ │ │ │ │ │ ├── rst │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── rst.js │ │ │ │ │ │ ├── ruby │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── ruby.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── rust │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── rust.js │ │ │ │ │ │ ├── sass │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── sass.js │ │ │ │ │ │ ├── scheme │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── scheme.js │ │ │ │ │ │ ├── shell │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── shell.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── sieve │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── sieve.js │ │ │ │ │ │ ├── slim │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── slim.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── smalltalk │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── smalltalk.js │ │ │ │ │ │ ├── smarty │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── smarty.js │ │ │ │ │ │ ├── smartymixed │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── smartymixed.js │ │ │ │ │ │ ├── solr │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── solr.js │ │ │ │ │ │ ├── soy │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── soy.js │ │ │ │ │ │ ├── sparql │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── sparql.js │ │ │ │ │ │ ├── spreadsheet │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── spreadsheet.js │ │ │ │ │ │ ├── sql │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── sql.js │ │ │ │ │ │ ├── stex │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── stex.js │ │ │ │ │ │ │ └── test.js │ │ │ │ │ │ ├── stylus │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── stylus.js │ │ │ │ │ │ ├── tcl │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── tcl.js │ │ │ │ │ │ ├── textile │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── test.js │ │ │ │ │ │ │ └── textile.js │ │ │ │ │ │ ├── tiddlywiki │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── tiddlywiki.css │ │ │ │ │ │ │ └── tiddlywiki.js │ │ │ │ │ │ ├── tiki │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── tiki.css │ │ │ │ │ │ │ └── tiki.js │ │ │ │ │ │ ├── toml │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── toml.js │ │ │ │ │ │ ├── tornado │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── tornado.js │ │ │ │ │ │ ├── turtle │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── turtle.js │ │ │ │ │ │ ├── vb │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── vb.js │ │ │ │ │ │ ├── vbscript │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── vbscript.js │ │ │ │ │ │ ├── velocity │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── velocity.js │ │ │ │ │ │ ├── verilog │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── test.js │ │ │ │ │ │ │ └── verilog.js │ │ │ │ │ │ ├── xml │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── test.js │ │ │ │ │ │ │ └── xml.js │ │ │ │ │ │ ├── xquery │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ ├── test.js │ │ │ │ │ │ │ └── xquery.js │ │ │ │ │ │ ├── yaml │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── yaml.js │ │ │ │ │ │ └── z80 │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ └── z80.js │ │ │ │ │ ├── modes.min.js │ │ │ │ │ ├── package.json │ │ │ │ │ └── theme │ │ │ │ │ │ ├── 3024-day.css │ │ │ │ │ │ ├── 3024-night.css │ │ │ │ │ │ ├── ambiance-mobile.css │ │ │ │ │ │ ├── ambiance.css │ │ │ │ │ │ ├── base16-dark.css │ │ │ │ │ │ ├── base16-light.css │ │ │ │ │ │ ├── blackboard.css │ │ │ │ │ │ ├── cobalt.css │ │ │ │ │ │ ├── colorforth.css │ │ │ │ │ │ ├── eclipse.css │ │ │ │ │ │ ├── elegant.css │ │ │ │ │ │ ├── erlang-dark.css │ │ │ │ │ │ ├── lesser-dark.css │ │ │ │ │ │ ├── mbo.css │ │ │ │ │ │ ├── mdn-like.css │ │ │ │ │ │ ├── midnight.css │ │ │ │ │ │ ├── monokai.css │ │ │ │ │ │ ├── neat.css │ │ │ │ │ │ ├── neo.css │ │ │ │ │ │ ├── night.css │ │ │ │ │ │ ├── paraiso-dark.css │ │ │ │ │ │ ├── paraiso-light.css │ │ │ │ │ │ ├── pastel-on-dark.css │ │ │ │ │ │ ├── rubyblue.css │ │ │ │ │ │ ├── solarized.css │ │ │ │ │ │ ├── the-matrix.css │ │ │ │ │ │ ├── tomorrow-night-bright.css │ │ │ │ │ │ ├── tomorrow-night-eighties.css │ │ │ │ │ │ ├── twilight.css │ │ │ │ │ │ ├── vibrant-ink.css │ │ │ │ │ │ ├── xq-dark.css │ │ │ │ │ │ ├── xq-light.css │ │ │ │ │ │ └── zenburn.css │ │ │ │ ├── flowchart.min.js │ │ │ │ ├── jquery.flowchart.min.js │ │ │ │ ├── marked.min.js │ │ │ │ ├── prettify.min.js │ │ │ │ ├── raphael.min.js │ │ │ │ ├── sequence-diagram.min.js │ │ │ │ └── underscore.min.js │ │ │ ├── plugins │ │ │ │ ├── code-block-dialog │ │ │ │ │ └── code-block-dialog.js │ │ │ │ ├── emoji-dialog │ │ │ │ │ ├── emoji-dialog.js │ │ │ │ │ └── emoji.json │ │ │ │ ├── goto-line-dialog │ │ │ │ │ └── goto-line-dialog.js │ │ │ │ ├── help-dialog │ │ │ │ │ ├── help-dialog.js │ │ │ │ │ └── help.md │ │ │ │ ├── html-entities-dialog │ │ │ │ │ ├── html-entities-dialog.js │ │ │ │ │ └── html-entities.json │ │ │ │ ├── image-dialog │ │ │ │ │ └── image-dialog.js │ │ │ │ ├── link-dialog │ │ │ │ │ └── link-dialog.js │ │ │ │ ├── plugin-template.js │ │ │ │ ├── preformatted-text-dialog │ │ │ │ │ └── preformatted-text-dialog.js │ │ │ │ ├── reference-link-dialog │ │ │ │ │ └── reference-link-dialog.js │ │ │ │ ├── table-dialog │ │ │ │ │ └── table-dialog.js │ │ │ │ └── test-plugin │ │ │ │ │ └── test-plugin.js │ │ │ └── scss │ │ │ │ ├── editormd.codemirror.scss │ │ │ │ ├── editormd.dialog.scss │ │ │ │ ├── editormd.form.scss │ │ │ │ ├── editormd.grid.scss │ │ │ │ ├── editormd.logo.scss │ │ │ │ ├── editormd.menu.scss │ │ │ │ ├── editormd.preview.scss │ │ │ │ ├── editormd.preview.themes.scss │ │ │ │ ├── editormd.scss │ │ │ │ ├── editormd.tab.scss │ │ │ │ ├── editormd.themes.scss │ │ │ │ ├── font-awesome.scss │ │ │ │ ├── github-markdown.scss │ │ │ │ ├── lib │ │ │ │ ├── prefixes.scss │ │ │ │ └── variables.scss │ │ │ │ └── prettify.scss │ │ ├── fonts │ │ │ └── Heebo │ │ │ │ ├── Heebo-Black.ttf │ │ │ │ ├── Heebo-Bold.ttf │ │ │ │ ├── Heebo-ExtraBold.ttf │ │ │ │ ├── Heebo-ExtraLight.ttf │ │ │ │ ├── Heebo-Light.ttf │ │ │ │ ├── Heebo-Medium.ttf │ │ │ │ ├── Heebo-Regular.ttf │ │ │ │ ├── Heebo-SemiBold.ttf │ │ │ │ ├── Heebo-Thin.ttf │ │ │ │ ├── NGS6v5_NC0k9P9H0TbFhsqMA6aw.woff2 │ │ │ │ ├── NGS6v5_NC0k9P9H0TbFzsQ.woff2 │ │ │ │ └── NGS6v5_NC0k9P9H2TbE.woff2 │ │ ├── img │ │ │ ├── admin_icon.svg │ │ │ ├── board_icon.svg │ │ │ ├── bootstrap-icons.svg │ │ │ ├── calendar_icon.svg │ │ │ ├── empty.svg │ │ │ ├── gantt_icon.svg │ │ │ ├── github.svg │ │ │ ├── google.svg │ │ │ ├── icon-attach.svg │ │ │ ├── icon-attachment.svg │ │ │ ├── icon-check-o.svg │ │ │ ├── icon-check.svg │ │ │ ├── icon-link.svg │ │ │ ├── icon-tasks.svg │ │ │ ├── icon-time.svg │ │ │ ├── issues_icon.svg │ │ │ ├── jd_fav.png │ │ │ ├── jd_logo.png │ │ │ ├── loading_spinner.gif │ │ │ ├── manage_icon.svg │ │ │ ├── microsoft.svg │ │ │ ├── mono.png │ │ │ ├── mono.svg │ │ │ ├── not_found.svg │ │ │ ├── notes_icon.svg │ │ │ ├── priority-blocker.svg │ │ │ ├── priority-high.svg │ │ │ ├── priority-low.svg │ │ │ ├── priority-normal.svg │ │ │ └── report_icon.svg │ │ ├── js │ │ │ ├── chart.min.js │ │ │ ├── chartjs-adapter-date-fns.bundle.min.js │ │ │ ├── dataTables.bootstrap4.min.js │ │ │ ├── scripts.js │ │ │ ├── scripts_alt.js │ │ │ └── showdown.min.js │ │ └── less │ │ │ └── style.less │ ├── custom.scss │ ├── environments │ │ ├── environment.prod.ts │ │ ├── environment.test.ts │ │ └── environment.ts │ ├── index.html │ ├── main.ts │ ├── manifest.webmanifest │ ├── polyfills.ts │ ├── proxy.conf.json │ ├── styles.css │ ├── test.ts │ ├── theme.less │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json ├── LICENSE ├── README.md ├── docker-compose-custom-db.yaml ├── docker-compose-dev.yaml ├── docker-compose.yaml ├── docs ├── .nojekyll ├── README.md ├── _sidebar.md ├── admin_project_manage.md ├── admin_user_manage.md ├── create_update_video.html ├── img │ ├── Board.jpg │ ├── Calendar.jpg │ ├── Dashboard.jpg │ ├── End.jpg │ ├── Gantt.png │ ├── Issue-View.jpg │ ├── Issues.jpg │ ├── Overview.jpg │ ├── PermMgmt.jpg │ ├── Reporting.jpg │ ├── Workflow.jpg │ ├── admin_create_project.png │ ├── admin_create_project_details.png │ ├── after_admin_initial_login.png │ ├── board-view.png │ ├── calendar-view.png │ ├── component.png │ ├── create_issue.png │ ├── gantt-view.png │ ├── issue-search-screen.png │ ├── issue_notification.png │ ├── jd_logo.png │ ├── login.png │ ├── manage_issue_types.png │ ├── pages-create.png │ ├── pages-view.png │ ├── projects_list.png │ ├── setup.png │ └── time_tracking.png ├── index.html ├── initial_setup_video.html ├── installation.md ├── issue_features_video.html ├── issue_manage.md ├── pages.md ├── reporting.md ├── setup.md └── workflow_manage.md └── init-db.sh /.env: -------------------------------------------------------------------------------- 1 | APP_DOMAIN=joindeskapp.com 2 | 3 | POSTGRES_USER=jdroot 4 | POSTGRES_PASSWORD=Reset#123 5 | 6 | POSTGRES_HOST=db 7 | POSTGRES_PORT=5432 8 | POSTGRES_DB=jdapp 9 | POSTGRES_NON_ROOT_USER=jduser 10 | POSTGRES_NON_ROOT_PASSWORD=Reset@123 11 | 12 | JD_DATA_DIR=/var/jd/ 13 | JD_DATA_TEMP_DIR=/var/jd/upload_tmp 14 | JD_MAX_FILE_SIZE=25MB 15 | 16 | JD_EMAIL=false 17 | JD_EMAIL_HOST=smtp.domain.com 18 | JD_EMAIL_PORT=587 19 | JD_EMAIL_USERNAME=no-reply@joindeskapp.com 20 | JD_EMAIL_PASSWORD=password 21 | JD_EMAIL_SMTP_AUTH=true 22 | JD_EMAIL_SMTP_STARTTLS_ENABLE=true 23 | JD_EMAIL_SMTP_STARTTLS_REQUIRED=true 24 | JD_EMAIL_SSL_TRUST=smtp.domain.com -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Frontend/dist/ 2 | -------------------------------------------------------------------------------- /Backend/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### Hide logs 14 | *.gz 15 | *.log 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /build/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ -------------------------------------------------------------------------------- /Backend/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joindesk/Joindesk/84f8b0cd7ce56b3191bc5bdaabfc66d1c7c890dc/Backend/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /Backend/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip 2 | -------------------------------------------------------------------------------- /Backend/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "type": "java", 5 | "name": "Spring Boot-JoindeskApplication", 6 | "request": "launch", 7 | "cwd": "${workspaceFolder}", 8 | "console": "internalConsole", 9 | "mainClass": "com.ariseontech.joindesk.JoindeskApplication", 10 | "projectName": "joindesk", 11 | "args": "" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /Backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:11-jre-slim-buster 2 | EXPOSE 9000 3 | ARG JAR_FILE=target/joindesk-*.jar 4 | ADD ${JAR_FILE} app.jar 5 | ENTRYPOINT ["java","-jar","/app.jar"] -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/Authority.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import javax.validation.constraints.NotEmpty; 7 | import javax.validation.constraints.Size; 8 | 9 | @Entity 10 | @Data 11 | public class Authority { 12 | 13 | @Id 14 | @GeneratedValue(strategy = GenerationType.AUTO) 15 | private Long id; 16 | 17 | @NotEmpty 18 | @Size(min = 4) 19 | private String name; 20 | 21 | private String description; 22 | 23 | @Enumerated(EnumType.STRING) 24 | private AuthorityCode authority; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/AuthorityCode.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | public enum AuthorityCode { 4 | 5 | PROJECT_MANAGE, 6 | 7 | PROJECT_VIEW, 8 | 9 | ISSUE_ASSIGN, 10 | 11 | ISSUE_CREATE, 12 | 13 | ISSUE_EDIT, 14 | 15 | ISSUE_DELETE, 16 | 17 | ISSUE_LINK, 18 | 19 | ISSUE_MODIFY_REPORTER, 20 | 21 | ISSUE_MOVE, 22 | 23 | ISSUE_TRANSITION, 24 | 25 | COMMENT_ADD, 26 | 27 | COMMENT_DELETE_ALL, 28 | 29 | COMMENT_DELETE_OWN, 30 | 31 | COMMENT_EDIT_ALL, 32 | 33 | COMMENT_EDIT_OWN, 34 | 35 | ATTACHMENT_CREATE, 36 | 37 | ATTACHMENT_DELETE_ALL, 38 | 39 | ATTACHMENT_DELETE_OWN, 40 | 41 | LOG_WORK, 42 | 43 | WIKI_VIEW, 44 | 45 | WIKI_EDIT, 46 | 47 | WIKI_DELETE 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/AuthorityGlobal.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.hibernate.annotations.UpdateTimestamp; 7 | 8 | import javax.persistence.*; 9 | import javax.validation.constraints.NotNull; 10 | import java.time.LocalDateTime; 11 | 12 | @Entity 13 | @NoArgsConstructor 14 | @Data 15 | @Table(indexes = { 16 | @Index(name = "JD_AUTH_G_IDX", columnList = "login") 17 | }) 18 | public class AuthorityGlobal { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) 22 | @JsonIgnore 23 | private Long id; 24 | 25 | @NotNull 26 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 27 | @JoinColumn(name = "login") 28 | private Login login; 29 | 30 | @Enumerated(EnumType.STRING) 31 | private AuthorityCode authorityCode; 32 | 33 | @UpdateTimestamp 34 | private LocalDateTime lastUpdated; 35 | } 36 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/ChangePasswordDTO.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ChangePasswordDTO { 7 | String currentPassword, newPassword, confirmNewPassword; 8 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/ForgotPasswordRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ForgotPasswordRequestDTO { 7 | String user; 8 | String otp; 9 | String password, passwordc; 10 | int stage; 11 | } 12 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/IPWhiteBlacklist.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.GeneratedValue; 11 | import javax.persistence.GenerationType; 12 | import javax.persistence.Id; 13 | import javax.validation.constraints.NotEmpty; 14 | 15 | @Entity 16 | @Data 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @EqualsAndHashCode(callSuper = false) 20 | public class IPWhiteBlacklist extends AuditModel { 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.AUTO) 23 | private Long id; 24 | 25 | @NotEmpty(message = "cidr is required") 26 | private String cidr; 27 | 28 | @NotEmpty(message = "Description is required") 29 | private String description; 30 | 31 | private boolean enabled = false; 32 | 33 | private boolean apiOnly = false; 34 | 35 | private boolean whiteList = false; 36 | } 37 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/LoginRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import com.ariseontech.joindesk.auth.domain.PreferredAuthTypes; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class LoginRequestDTO { 8 | private String username; 9 | private String password; 10 | private String token; 11 | private String confirmPassword; 12 | private boolean remember; 13 | private String deviceInfo; 14 | private String deviceFp; 15 | private PreferredAuthTypes mode; 16 | } 17 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/PasswordResetToken.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.*; 9 | import javax.validation.constraints.NotNull; 10 | import java.time.LocalDateTime; 11 | 12 | @Entity 13 | @NoArgsConstructor 14 | @Data 15 | @EqualsAndHashCode(callSuper = false) 16 | public class PasswordResetToken extends AuditModel { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.AUTO) 20 | private Long id; 21 | 22 | @NotNull 23 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 24 | @JoinColumn(name = "user_id") 25 | private Login login; 26 | 27 | private String otp; 28 | 29 | private LocalDateTime expiry; 30 | 31 | private LocalDateTime lastSent; 32 | } 33 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/PreferredAuthTypes.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | public enum PreferredAuthTypes { 4 | EMAIL, 5 | SLACK, 6 | MFA 7 | } 8 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/domain/SlackUser.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class SlackUser { 11 | String id; 12 | String name; 13 | String email; 14 | } 15 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/repo/AuthorityGlobalRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.repo; 2 | 3 | 4 | import com.ariseontech.joindesk.auth.domain.AuthorityCode; 5 | import com.ariseontech.joindesk.auth.domain.AuthorityGlobal; 6 | import com.ariseontech.joindesk.auth.domain.Login; 7 | import org.springframework.data.jpa.repository.JpaRepository; 8 | import org.springframework.data.jpa.repository.Modifying; 9 | 10 | import javax.transaction.Transactional; 11 | import java.util.List; 12 | 13 | public interface AuthorityGlobalRepo extends JpaRepository { 14 | 15 | List findByLogin(Login l); 16 | 17 | List findByAuthorityCode(AuthorityCode code); 18 | 19 | @Transactional 20 | @Modifying 21 | void deleteByLogin(Login l); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/repo/AuthorityProjectRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.repo; 2 | 3 | 4 | import com.ariseontech.joindesk.auth.domain.AuthorityCode; 5 | import com.ariseontech.joindesk.auth.domain.AuthorityProject; 6 | import com.ariseontech.joindesk.auth.domain.Login; 7 | import com.ariseontech.joindesk.project.domain.Project; 8 | import org.springframework.data.jpa.repository.JpaRepository; 9 | import org.springframework.data.jpa.repository.Modifying; 10 | 11 | import javax.transaction.Transactional; 12 | import java.util.List; 13 | 14 | public interface AuthorityProjectRepo extends JpaRepository { 15 | 16 | List findByProjectAndLogin(Project p, Login l); 17 | 18 | List findByProject(Project p); 19 | 20 | List findByProjectAndAuthorityCode(Project p, AuthorityCode code); 21 | 22 | List findByLogin(Login l); 23 | 24 | @Transactional 25 | @Modifying 26 | void deleteByLogin(Login l); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/repo/IPWhiteBlackListRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.repo; 2 | 3 | 4 | import com.ariseontech.joindesk.auth.domain.IPWhiteBlacklist; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface IPWhiteBlackListRepo extends JpaRepository { 10 | 11 | Set findByEnabledTrue(); 12 | 13 | Set findByEnabledTrueAndWhiteListTrue(); 14 | 15 | Set findByEnabledTrueAndWhiteListFalse(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/repo/LoginRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.repo; 2 | 3 | 4 | import com.ariseontech.joindesk.auth.domain.Login; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | 8 | import java.util.Set; 9 | 10 | public interface LoginRepo extends JpaRepository { 11 | 12 | Set findAllByOrderByEmailAsc(); 13 | 14 | Login findByEmailIgnoreCase(String email); 15 | 16 | @Query(value = "select email from jd_user", nativeQuery = true) 17 | Set getEmails(); 18 | 19 | Set findByActiveTrue(); 20 | 21 | Set findBySuperAdminTrue(); 22 | 23 | Login findByUserNameIgnoreCase(String userName); 24 | 25 | Login findByApiToken(String token); 26 | 27 | Login findBySlackID(String slackID); 28 | } 29 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/repo/PasswordResetTokenRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.repo; 2 | 3 | import com.ariseontech.joindesk.auth.domain.Login; 4 | import com.ariseontech.joindesk.auth.domain.PasswordResetToken; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import javax.transaction.Transactional; 8 | import java.time.LocalDateTime; 9 | 10 | public interface PasswordResetTokenRepo extends JpaRepository { 11 | 12 | @Transactional 13 | void deleteByLoginAndExpiryBefore(Login login, LocalDateTime now); 14 | 15 | PasswordResetToken findByLogin(Login login); 16 | 17 | PasswordResetToken findByLoginAndOtp(Login login, String otp); 18 | 19 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/repo/TokenRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.repo; 2 | 3 | import com.ariseontech.joindesk.auth.domain.Login; 4 | import com.ariseontech.joindesk.auth.domain.Token; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface TokenRepo extends JpaRepository { 10 | 11 | Token findByToken(String token); 12 | 13 | Set findAllByUser(Login l); 14 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/service/LoginDetail.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.service; 2 | 3 | public class LoginDetail { 4 | } 5 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/util/AuditorAwareImpl.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.util; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.data.domain.AuditorAware; 5 | 6 | import java.util.Optional; 7 | 8 | public class AuditorAwareImpl implements AuditorAware { 9 | @Autowired 10 | private CurrentLogin currentLogin; 11 | 12 | public Optional getCurrentAuditor() { 13 | if (currentLogin != null && currentLogin.getUser() != null) 14 | return Optional.ofNullable(currentLogin.getUser().getUserName()); 15 | else return Optional.empty(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/util/CurrentLogin.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.util; 2 | 3 | import com.ariseontech.joindesk.auth.domain.AuthorityGlobal; 4 | import com.ariseontech.joindesk.auth.domain.AuthorityProject; 5 | import com.ariseontech.joindesk.auth.domain.Login; 6 | import com.fasterxml.jackson.annotation.JsonIgnore; 7 | import lombok.Data; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.List; 11 | 12 | 13 | @Component 14 | @Data 15 | public class CurrentLogin { 16 | 17 | // Actual logged in user 18 | private Login user; 19 | 20 | // API token for Temp storage 21 | @JsonIgnore 22 | private String token; 23 | 24 | private List authorities; 25 | 26 | private List authoritiesGlobal; 27 | 28 | public static String trimDoubleQuotes(String text) { 29 | int textLength = text.length(); 30 | 31 | if (textLength >= 2 && text.charAt(0) == '"' && text.charAt(textLength - 1) == '"') { 32 | return text.substring(1, textLength - 1); 33 | } 34 | 35 | return text.replaceAll("\\\\", ""); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/auth/util/View.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.auth.util; 2 | 3 | public class View { 4 | public interface Public { 5 | } 6 | 7 | public interface Details extends Public { 8 | } 9 | 10 | public interface Report extends Public { 11 | } 12 | 13 | public interface Admin extends Details { 14 | } 15 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/board/repo/BoardRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.board.repo; 2 | 3 | import com.ariseontech.joindesk.board.domain.Board; 4 | import com.ariseontech.joindesk.issues.domain.IssueFilter; 5 | import com.ariseontech.joindesk.project.domain.Project; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | 9 | import java.util.Set; 10 | 11 | public interface BoardRepo extends JpaRepository { 12 | 13 | Set findAllByProject(Project project); 14 | 15 | Set findAllByFilter(IssueFilter filter); 16 | 17 | Set findAllByProjectAndActiveTrue(Project project); 18 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/board/repo/LaneRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.board.repo; 2 | 3 | import com.ariseontech.joindesk.board.domain.Board; 4 | import com.ariseontech.joindesk.board.domain.Lane; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface LaneRepo extends JpaRepository { 10 | Set findAllByBoardOrderByLaneOrderAsc(Board board); 11 | 12 | int countByBoard(Board board); 13 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/event/domain/IssueEventType.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.event.domain; 2 | 3 | public enum IssueEventType { 4 | CREATE, 5 | UPDATE, 6 | ASSIGN, 7 | REASSIGN, 8 | COMMENT, 9 | MENTION, 10 | COMMENT_DELETE, 11 | ATTACH, 12 | ATTACH_DELETE, 13 | DELETE, 14 | DUE, 15 | VIEW 16 | } 17 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/event/domain/JDEventType.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.event.domain; 2 | 3 | public enum JDEventType { 4 | ISSUE_CREATE, 5 | ISSUE_UPDATE, 6 | ISSUE_DELETE, 7 | ISSUE_COMMENT_CREATE, 8 | ISSUE_COMMENT_UPDATE, 9 | ISSUE_COMMENT_DELETE, 10 | ISSUE_ASSIGN, 11 | ISSUE_ATTACHMENT_CREATE, 12 | ISSUE_ATTACHMENT_DELETE 13 | } 14 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/event/domain/Subscription.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.event.domain; 2 | 3 | import com.github.bohnman.squiggly.view.PropertyView; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.GenerationType; 10 | import javax.persistence.Id; 11 | 12 | @Data 13 | @Entity 14 | @NoArgsConstructor 15 | public class Subscription { 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.AUTO) 18 | private Long id; 19 | 20 | @PropertyView("secret") 21 | private String hookurl; 22 | 23 | private JDEventType type; 24 | 25 | private String projectKey; 26 | 27 | @PropertyView("secret") 28 | private Long loginID; 29 | 30 | public Subscription(String hookurl, JDEventType type, String projectKey, Long loginID) { 31 | this.hookurl = hookurl; 32 | this.type = type; 33 | this.loginID = loginID; 34 | this.projectKey = projectKey; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/event/repo/IssueEventRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.event.repo; 2 | 3 | import com.ariseontech.joindesk.event.domain.IssueEvent; 4 | import com.ariseontech.joindesk.issues.domain.Issue; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Modifying; 7 | 8 | import javax.transaction.Transactional; 9 | 10 | public interface IssueEventRepo extends JpaRepository { 11 | @Transactional 12 | @Modifying 13 | void deleteByIssue(Issue issue); 14 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/event/repo/JDEventRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.event.repo; 2 | 3 | import com.ariseontech.joindesk.event.domain.JDEvent; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface JDEventRepo extends JpaRepository { 7 | 8 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/event/repo/SubscriptionRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.event.repo; 2 | 3 | import com.ariseontech.joindesk.event.domain.JDEventType; 4 | import com.ariseontech.joindesk.event.domain.Subscription; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.List; 8 | 9 | public interface SubscriptionRepo extends JpaRepository { 10 | 11 | List findByLoginID(Long loginID); 12 | 13 | List findByLoginIDAndTypeAndProjectKey(Long loginID, JDEventType type, String projectKey); 14 | 15 | List findByTypeAndProjectKey(JDEventType type, String projectKey); 16 | 17 | Subscription findByIdAndTypeAndProjectKey(Long id, JDEventType type, String projectKey); 18 | 19 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/exception/ErrorDetails.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.exception; 2 | 3 | import lombok.Data; 4 | 5 | import java.sql.Timestamp; 6 | import java.util.Date; 7 | import java.util.List; 8 | 9 | @Data 10 | public class ErrorDetails { 11 | 12 | private Date timestamp; 13 | private String message; 14 | private ErrorCode error; 15 | private List errors; 16 | 17 | public ErrorDetails(String message, ErrorCode error) { 18 | super(); 19 | this.timestamp = new Timestamp(new Date().getTime()); 20 | if (message != null) this.message = message; 21 | else this.message = error.getDetails(); 22 | this.error = error; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/exception/JDException.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.exception; 2 | 3 | import lombok.Data; 4 | import org.springframework.http.HttpStatus; 5 | 6 | @Data 7 | public class JDException extends RuntimeException { 8 | 9 | private ErrorCode errorCode; 10 | 11 | private HttpStatus httpStatus; 12 | 13 | public JDException(String message, ErrorCode errorCode, HttpStatus httpStatus) { 14 | super(message); 15 | this.httpStatus = httpStatus; 16 | this.errorCode = errorCode; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/exception/JDTransitionException.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.exception; 2 | 3 | import lombok.Data; 4 | import org.springframework.http.HttpStatus; 5 | 6 | @Data 7 | public class JDTransitionException extends RuntimeException { 8 | 9 | private ErrorCode errorCode; 10 | 11 | private String response; 12 | 13 | private HttpStatus httpStatus; 14 | 15 | public JDTransitionException(String response, ErrorCode errorCode, HttpStatus httpStatus) { 16 | super(response); 17 | this.httpStatus = httpStatus; 18 | this.errorCode = errorCode; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/domain/GitHook.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.NotEmpty; 11 | import javax.validation.constraints.NotNull; 12 | import java.io.Serializable; 13 | 14 | @Entity 15 | @NoArgsConstructor 16 | @Data 17 | @EqualsAndHashCode(callSuper = false) 18 | @Table(indexes = { 19 | @Index(name = "JD_GIT_HOOK_IDX", columnList = "repository") 20 | }) 21 | public class GitHook extends AuditModel implements Serializable { 22 | 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | private Long id; 26 | 27 | @NotEmpty 28 | private String hookName; 29 | 30 | @Column(unique = true) 31 | private String uuid; 32 | 33 | @NotNull 34 | @OneToOne(cascade = CascadeType.DETACH) 35 | @JoinColumn(name = "repository") 36 | @JsonIgnore 37 | private Repository repository; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/domain/GitRepoType.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.domain; 2 | 3 | public enum GitRepoType { 4 | GITHUB, 5 | GITLAB 6 | } 7 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/repo/GitBranchRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.repo; 2 | 3 | import com.ariseontech.joindesk.git.domain.GitBranch; 4 | import com.ariseontech.joindesk.git.domain.Repository; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface GitBranchRepo extends JpaRepository { 10 | Set findByIssuesContainingIgnoreCase(String issueKey); 11 | 12 | Long countByIssuesContainingIgnoreCase(String issueKey); 13 | 14 | GitBranch findByNameAndRepository(String name, Repository repository); 15 | 16 | GitBranch findByName(String name); 17 | 18 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/repo/GitCommitRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.repo; 2 | 3 | import com.ariseontech.joindesk.git.domain.GitCommit; 4 | import com.ariseontech.joindesk.git.domain.Repository; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface GitCommitRepo extends JpaRepository { 10 | 11 | Set findByIssuesContainingIgnoreCase(String issueKey); 12 | 13 | Long countByIssuesContainingIgnoreCase(String issueKey); 14 | 15 | GitCommit findByCommitIdAndRepository(String commitId, Repository repository); 16 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/repo/HookRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.repo; 2 | 3 | import com.ariseontech.joindesk.git.domain.GitHook; 4 | import com.ariseontech.joindesk.git.domain.Repository; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface HookRepo extends JpaRepository { 10 | 11 | Set findAllByRepository(Repository repository); 12 | 13 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/repo/RepositoryRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.repo; 2 | 3 | import com.ariseontech.joindesk.git.domain.Repository; 4 | import com.ariseontech.joindesk.project.domain.Project; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface RepositoryRepo extends JpaRepository { 10 | 11 | Set findAllByProject(Project project); 12 | 13 | Repository findByUuid(String uuid); 14 | 15 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/git/web/GitHookController.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.git.web; 2 | 3 | import com.ariseontech.joindesk.SystemInfo; 4 | import com.ariseontech.joindesk.git.service.GitHookService; 5 | import com.ariseontech.joindesk.git.service.GitRepositoryService; 6 | import com.ariseontech.joindesk.project.service.ProjectService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | 12 | @RestController 13 | @RequestMapping(value = SystemInfo.apiPrefix + "/hook/git/", produces = "application/json", consumes = "application/json") 14 | public class GitHookController { 15 | 16 | @Autowired 17 | private GitRepositoryService gitRepositoryService; 18 | @Autowired 19 | private GitHookService gitHookService; 20 | @Autowired 21 | private ProjectService projectService; 22 | 23 | @PostMapping("{uuid}") 24 | public void receiveHooks(@PathVariable("uuid") String repoId, @RequestBody String payload, 25 | HttpServletRequest request) { 26 | gitHookService.receiveHook(repoId, payload, request); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/Attachment.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.NotNull; 11 | 12 | @Entity 13 | @NoArgsConstructor 14 | @Data 15 | @EqualsAndHashCode(callSuper = false, exclude = "issue") 16 | @Table(indexes = { 17 | @Index(name = "JD_ATTACHM_IDX", columnList = "issue") 18 | }) 19 | public class Attachment extends AuditModel { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | private Long id; 24 | 25 | @NotNull 26 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) 27 | @JoinColumn(name = "issue") 28 | @JsonIgnore 29 | private Issue issue; 30 | 31 | private String name, originalName, type, thumbnail; 32 | 33 | @Transient 34 | private boolean previewable = false; 35 | 36 | private Long size; 37 | 38 | @Transient 39 | private String location; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/FilteredIssueDTO.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | @Data 8 | public class FilteredIssueDTO { 9 | long total, pageSize, pageIndex; 10 | List issues; 11 | } 12 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueCustomField.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import com.ariseontech.joindesk.project.domain.CustomField; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | 10 | import javax.persistence.*; 11 | 12 | @Data 13 | @EqualsAndHashCode(callSuper = false) 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class IssueCustomField extends AuditModel { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Long id; 21 | 22 | private CustomField customField; 23 | 24 | private String value; 25 | 26 | @Deprecated(forRemoval = true) 27 | private String[] values; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueFilterOptions.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | public 8 | class IssueFilterOptions { 9 | private String name, value; 10 | 11 | public IssueFilterOptions(String value, String name) { 12 | this.name = name; 13 | this.value = value; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueLifeCycle.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | public enum IssueLifeCycle { 4 | 5 | TODO, INPROGRESS, DONE, REVIEW, ALERT, HIGHLIGHT 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueMentions.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.*; 10 | 11 | @Data 12 | @EqualsAndHashCode(callSuper = false) 13 | @Entity 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Table(indexes = { 17 | @Index(name = "JD_I_MENT_IDX", columnList = "issue") 18 | }) 19 | public class IssueMentions extends AuditModel { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | private Long id; 24 | 25 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 26 | @JoinColumn(name = "issue") 27 | private Issue issue; 28 | 29 | private boolean link, mention; 30 | 31 | private String linkURL; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueOtherRelationship.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.*; 9 | 10 | @Data 11 | @EqualsAndHashCode(callSuper = false) 12 | @Entity 13 | @NoArgsConstructor 14 | public class IssueOtherRelationship extends AuditModel { 15 | 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private Long id; 19 | 20 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 21 | @JoinColumn(name = "issue") 22 | private Issue issue; 23 | 24 | private String link, label; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueRelationship.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.*; 9 | 10 | @Data 11 | @EqualsAndHashCode(callSuper = false) 12 | @Entity 13 | @NoArgsConstructor 14 | public class IssueRelationship extends AuditModel { 15 | 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private Long id; 19 | 20 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 21 | @JoinColumn(name = "from_issue") 22 | private Issue fromIssue; 23 | 24 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 25 | @JoinColumn(name = "to_issue") 26 | private Issue toIssue; 27 | 28 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 29 | @JoinColumn(name = "rel_type") 30 | private Relationship type; 31 | 32 | @Transient 33 | private String fromIssuePair, toIssuePair; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueSearchDTO.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class IssueSearchDTO { 12 | @JsonIgnore 13 | String projectKey; 14 | String sortBy, sortDir, timezone; 15 | IssueFilter filter; 16 | String jql; 17 | int pageIndex, pageSize; 18 | List issues; 19 | List issueKeys; 20 | long total; 21 | 22 | public IssueSearchDTO(String projectKey, String sortBy, String sortDir, String timezone, IssueFilter filter, int pageIndex, int pageSize) { 23 | this.projectKey = projectKey; 24 | this.sortBy = sortBy; 25 | this.sortDir = sortDir; 26 | this.timezone = timezone; 27 | this.filter = filter; 28 | this.pageIndex = pageIndex; 29 | this.pageSize = pageSize; 30 | } 31 | 32 | public IssueSearchDTO() { 33 | total = 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueSearchQuery.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import java.io.Serializable; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @Data 11 | @Entity 12 | public class IssueSearchQuery implements Serializable { 13 | @Id 14 | @GeneratedValue(strategy = GenerationType.IDENTITY) 15 | private Long id; 16 | private String condition = "and"; 17 | @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 18 | private List rules = new ArrayList<>(); 19 | 20 | IssueSearchQuery() { 21 | } 22 | 23 | public IssueSearchQuery(String condition, List rules) { 24 | this.condition = condition; 25 | this.rules = rules; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssueSearchQueryRule.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.persistence.*; 7 | import java.io.Serializable; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Data 12 | @Entity 13 | @NoArgsConstructor 14 | public class IssueSearchQueryRule implements Serializable { 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | private String field, operator, valueFrom, valueTo; 19 | private String[] values; 20 | @Transient 21 | private List expandedValues = new ArrayList<>(); 22 | 23 | public IssueSearchQueryRule(String field, String operator, String[] values) { 24 | this.field = field; 25 | this.operator = operator; 26 | this.values = values; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/IssuesFilterDTO.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | @Data 11 | public class IssuesFilterDTO { 12 | 13 | public long pageSize, pageIndex; 14 | 15 | public String containsText; 16 | 17 | public Map filterOptions; 18 | 19 | public Set selected; 20 | 21 | public String timezone; 22 | 23 | public IssueFilter filter; 24 | 25 | public Set sortOptions; 26 | 27 | public IssuesFilterDTO() { 28 | sortOptions = Set.of("Created", "Updated", "Due", "Assignee", "Reporter", "Key", "Priority"); 29 | pageSize = 10; 30 | pageIndex = 0; 31 | // TODO 32 | //resolutions.add(new Resolution("unresolved")); 33 | // TODO 34 | //assignee.add(new Login(0L, "unassigned", "", "unassigned")); 35 | selected = new HashSet<>(); 36 | selected.add("resolution"); 37 | selected.add("assignee"); 38 | filterOptions = new HashMap<>(); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/Label.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.NotBlank; 11 | import javax.validation.constraints.Size; 12 | 13 | @Entity 14 | @Data 15 | @EqualsAndHashCode(callSuper = false) 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class Label extends AuditModel { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.AUTO) 22 | private Long id; 23 | 24 | @NotBlank(message = "Name is required") 25 | @Size(min = 2) 26 | @Column(unique = true) 27 | private String name; 28 | } 29 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/Priority.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | public enum Priority { 4 | 5 | Urgent, High, Normal, Low 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/QuickUpdate.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | @Data 8 | public class QuickUpdate { 9 | String field, data; 10 | List issuesKeyPairs; 11 | } 12 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/Relationship.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import javax.persistence.*; 8 | import javax.validation.constraints.NotEmpty; 9 | 10 | @Data 11 | @EqualsAndHashCode(callSuper = false) 12 | @Entity 13 | public class Relationship extends AuditModel { 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | @NotEmpty 19 | @Column(unique = true, nullable = false) 20 | private String name; 21 | @NotEmpty 22 | @Column(unique = true, nullable = false) 23 | private String outwardDesc; 24 | @Column(unique = true) 25 | private String inwardDesc; 26 | 27 | public Relationship() { 28 | } 29 | 30 | public Relationship(Long id, @NotEmpty String name, @NotEmpty String outwardDesc, String inwardDesc) { 31 | this.id = id; 32 | this.name = name; 33 | this.outwardDesc = outwardDesc; 34 | this.inwardDesc = inwardDesc; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/Resolution.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.NotEmpty; 11 | 12 | @Data 13 | @Entity 14 | @EqualsAndHashCode(callSuper = false) 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class Resolution extends AuditModel { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | 23 | @NotEmpty 24 | @Column(unique = true) 25 | private String name; 26 | 27 | public Resolution(@NotEmpty String name) { 28 | this.name = name; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/WorkflowChange.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | @Data 11 | public class WorkflowChange { 12 | 13 | Map map; 14 | 15 | Set fromSteps; 16 | 17 | Set toSteps; 18 | 19 | public WorkflowChange() { 20 | this.map = new HashMap<>(); 21 | this.fromSteps = new HashSet<>(); 22 | this.toSteps = new HashSet<>(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/WorkflowStep.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | import com.ariseontech.joindesk.AuditModel; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | import lombok.ToString; 9 | 10 | import javax.persistence.*; 11 | import javax.validation.constraints.NotEmpty; 12 | import javax.validation.constraints.NotNull; 13 | 14 | @Entity 15 | @NoArgsConstructor 16 | @Data 17 | @EqualsAndHashCode(exclude = "workflow",callSuper = false) 18 | @ToString(exclude = "workflow") 19 | public class WorkflowStep extends AuditModel { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | private Long id; 24 | 25 | @NotNull 26 | @OneToOne(fetch = FetchType.LAZY) 27 | @JsonIgnore 28 | private Workflow workflow; 29 | 30 | @OneToOne(fetch = FetchType.EAGER) 31 | private IssueStatus issueStatus; 32 | 33 | public WorkflowStep(IssueStatus issueStatus, @NotEmpty(message = "Workflow is required") Workflow workflow) { 34 | this.issueStatus = issueStatus; 35 | this.workflow = workflow; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/WorkflowTransitionPropertySubTypes.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | public enum WorkflowTransitionPropertySubTypes { 4 | CONDITION_CURRENT_USER("User is"), 5 | CONDITION_IS_IN_GROUP("User is in group"), 6 | CONDITION_HAS_PERMISSION("User has permission"), 7 | CONDITION_FIELD_REQUIRED("Required check for field"), 8 | CONDITION_CHECKLIST_COMPLETE("Issue checklist is complete"), 9 | POST_FUNCTION_ASSIGN_TO_USER("Assign issue to user"), 10 | POST_FUNCTION_UPDATE_FIELD("Update issue field"); 11 | 12 | private String displayName; 13 | 14 | WorkflowTransitionPropertySubTypes(String displayName) { 15 | this.displayName = displayName; 16 | } 17 | 18 | public String displayName() { 19 | return displayName; 20 | } 21 | 22 | // Optionally and/or additionally, toString. 23 | @Override 24 | public String toString() { 25 | return displayName; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/domain/WorkflowTransitionPropertyTypes.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.domain; 2 | 3 | public enum WorkflowTransitionPropertyTypes { 4 | CONDITION, 5 | POST_FUNCTION 6 | } 7 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/AttachmentRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Attachment; 4 | import com.ariseontech.joindesk.issues.domain.Issue; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Modifying; 7 | 8 | import javax.transaction.Transactional; 9 | import java.util.Set; 10 | 11 | public interface AttachmentRepo extends JpaRepository { 12 | 13 | Set findByIssue(Issue issue); 14 | 15 | Attachment findByIssueAndName(Issue issue, String name); 16 | 17 | Attachment findByIssueAndId(Issue issue, Long id); 18 | 19 | Long countByIssue(Issue issue); 20 | 21 | @Transactional 22 | @Modifying 23 | void deleteByIssue(Issue issue); 24 | 25 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/CommentRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Comment; 4 | import com.ariseontech.joindesk.issues.domain.Issue; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface CommentRepo extends JpaRepository { 10 | 11 | Set findByIssueOrderByCreatedDateDesc(Issue issue); 12 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueFilterRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.auth.domain.Login; 4 | import com.ariseontech.joindesk.issues.domain.*; 5 | import com.ariseontech.joindesk.project.domain.Project; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.query.Param; 9 | 10 | import java.time.LocalDate; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | public interface IssueFilterRepo extends JpaRepository { 15 | 16 | List findByOwner(Login login); 17 | 18 | List findByOwnerOrOpenTrue(Login login); 19 | 20 | List findByOpenTrue(); 21 | 22 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueHistoryRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Issue; 4 | import com.ariseontech.joindesk.issues.domain.IssueHistory; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface IssueHistoryRepo extends JpaRepository { 10 | 11 | Set findByIssueOrderByUpdatedDesc(Issue issue); 12 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueMentionsRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Issue; 4 | import com.ariseontech.joindesk.issues.domain.IssueMentions; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface IssueMentionsRepo extends JpaRepository { 10 | 11 | Set findByLinkTrue(); 12 | 13 | Set findByMentionTrue(); 14 | 15 | Set findByIssue(Issue issue); 16 | 17 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueOtherRelationshipRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Issue; 4 | import com.ariseontech.joindesk.issues.domain.IssueOtherRelationship; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.Set; 8 | 9 | public interface IssueOtherRelationshipRepo extends JpaRepository { 10 | Set findByIssue(Issue issue); 11 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueRelationshipRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Issue; 4 | import com.ariseontech.joindesk.issues.domain.IssueRelationship; 5 | import com.ariseontech.joindesk.issues.domain.Relationship; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | import java.util.Set; 9 | 10 | public interface IssueRelationshipRepo extends JpaRepository { 11 | 12 | Set findByFromIssueOrToIssue(Issue fromIssue, Issue toIssue); 13 | 14 | Set findByTypeAndFromIssueAndToIssue(Relationship type, Issue fromIssue, Issue toIssue); 15 | 16 | Set findByType(Relationship relationship); 17 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueStatusRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.IssueStatus; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface IssueStatusRepo extends JpaRepository { 7 | 8 | IssueStatus findByName(String name); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueTypeRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Issue; 4 | import com.ariseontech.joindesk.issues.domain.IssueType; 5 | import com.ariseontech.joindesk.issues.domain.Workflow; 6 | import com.ariseontech.joindesk.project.domain.Project; 7 | import org.springframework.data.jpa.repository.JpaRepository; 8 | import org.springframework.data.jpa.repository.Query; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | public interface IssueTypeRepo extends JpaRepository { 14 | 15 | Set findByProjectOrderByNameAsc(Project project); 16 | 17 | @Query(value = "select * from issue_type where project IN (?1)", nativeQuery = true) 18 | Set findAllByProject(Set projects); 19 | 20 | IssueType findByNameAndProject(String name, Project project); 21 | 22 | Set findByIconUrlAndProject(String iconUrl, Project project); 23 | 24 | List findByWorkflowOrderByNameAsc(Workflow workflow); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/IssueViewRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.auth.domain.Login; 4 | import com.ariseontech.joindesk.issues.domain.Issue; 5 | import com.ariseontech.joindesk.issues.domain.IssueView; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | 9 | import javax.transaction.Transactional; 10 | import java.util.Set; 11 | 12 | public interface IssueViewRepo extends JpaRepository { 13 | 14 | Set findFirst10ByIssue(Issue issue); 15 | 16 | Set findFirst10ByWho(Login who); 17 | 18 | @Transactional 19 | @Modifying 20 | void deleteByIssueAndWho(Issue issue, Login who); 21 | 22 | @Transactional 23 | @Modifying 24 | void deleteByIssue(Issue issue); 25 | 26 | } -------------------------------------------------------------------------------- /Backend/src/main/java/com/ariseontech/joindesk/issues/repo/LabelRepo.java: -------------------------------------------------------------------------------- 1 | package com.ariseontech.joindesk.issues.repo; 2 | 3 | import com.ariseontech.joindesk.issues.domain.Label; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.Set; 8 | 9 | public interface LabelRepo extends JpaRepository { 10 | 11 | @Query("select u from Label u where lower(u.name) like lower(concat('%', ?1,'%'))") 12 | Set