├── .editorconfig ├── .eslintrc.cjs ├── .gitattributes ├── .github ├── BETA_NOTIFICATION.md ├── BETA_NOTIFICATION.md.license ├── ISSUE_TEMPLATE │ ├── ANNOUNCEMENT.yml │ ├── ANNOUNCEMENT.yml.license │ ├── BUG_REPORT.yml │ ├── BUG_REPORT.yml.license │ ├── FEATURE_REQUEST.yml │ ├── FEATURE_REQUEST.yml.license │ ├── config.yml │ └── config.yml.license ├── RELEASE_NOTIFICATION.md ├── RELEASE_NOTIFICATION.md.license ├── actions │ ├── get-polls-version │ │ └── action.yml │ ├── setup-composer │ │ └── action.yml │ ├── setup-node │ │ └── action.yml │ └── setup-server │ │ └── action.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── appstore-build-publish.yml │ ├── auto-merge.yml │ ├── codeql-analysis.yml │ ├── lint-eslint.yml │ ├── lint-info-xml.yml │ ├── lint-php-cs.yml │ ├── lint-php.yml │ ├── lint-prettier.yml │ ├── lock-closed-issues.yml │ ├── nodejs.yml │ ├── phpunit-mariadb.yml │ ├── phpunit-mysql.yml │ ├── phpunit-pgsql.yml │ ├── phpunit-sqlite.yml │ ├── pr-feedback.yml │ ├── psalm-matrix.yml │ ├── publish_alpha.yml │ ├── publish_beta.yml │ ├── publish_release.yml │ ├── reuse.yml │ └── stale.yml ├── .gitignore ├── .l10nignore ├── .php-cs-fixer.dist.php ├── .prettierignore ├── .prettierrc.json ├── .prettierrc.json.license ├── .stylelintignore ├── .tx └── config ├── AUTHORS.md ├── CHANGELOG.md ├── COPYING ├── LICENSES ├── AGPL-3.0-or-later.txt ├── CC0-1.0.txt └── MIT.txt ├── Makefile ├── README.md ├── REUSE.toml ├── _config.yml ├── appinfo ├── info.xml ├── info.xml.license └── routes.php ├── composer.json ├── composer.lock ├── docs └── API_v1.0.md ├── external.d.ts ├── img ├── app.svg ├── polls-dark.svg └── polls.svg ├── jsconfig.json ├── l10n ├── .gitkeep ├── af.js ├── af.json ├── ar.js ├── ar.json ├── ast.js ├── ast.json ├── bg.js ├── bg.json ├── br.js ├── br.json ├── ca.js ├── ca.json ├── cs.js ├── cs.json ├── da.js ├── da.json ├── de.js ├── de.json ├── de_DE.js ├── de_DE.json ├── el.js ├── el.json ├── en_GB.js ├── en_GB.json ├── eo.js ├── eo.json ├── es.js ├── es.json ├── es_419.js ├── es_419.json ├── es_AR.js ├── es_AR.json ├── es_CL.js ├── es_CL.json ├── es_CO.js ├── es_CO.json ├── es_CR.js ├── es_CR.json ├── es_DO.js ├── es_DO.json ├── es_EC.js ├── es_EC.json ├── es_GT.js ├── es_GT.json ├── es_HN.js ├── es_HN.json ├── es_MX.js ├── es_MX.json ├── es_NI.js ├── es_NI.json ├── es_PA.js ├── es_PA.json ├── es_PE.js ├── es_PE.json ├── es_PR.js ├── es_PR.json ├── es_PY.js ├── es_PY.json ├── es_SV.js ├── es_SV.json ├── es_UY.js ├── es_UY.json ├── et_EE.js ├── et_EE.json ├── eu.js ├── eu.json ├── fa.js ├── fa.json ├── fi.js ├── fi.json ├── fr.js ├── fr.json ├── ga.js ├── ga.json ├── gl.js ├── gl.json ├── he.js ├── he.json ├── hr.js ├── hr.json ├── hu.js ├── hu.json ├── id.js ├── id.json ├── is.js ├── is.json ├── it.js ├── it.json ├── ja.js ├── ja.json ├── ka.js ├── ka.json ├── ka_GE.js ├── ka_GE.json ├── ko.js ├── ko.json ├── lt_LT.js ├── lt_LT.json ├── lv.js ├── lv.json ├── mk.js ├── mk.json ├── nb.js ├── nb.json ├── nl.js ├── nl.json ├── nn_NO.js ├── nn_NO.json ├── oc.js ├── oc.json ├── pl.js ├── pl.json ├── pt_BR.js ├── pt_BR.json ├── pt_PT.js ├── pt_PT.json ├── ro.js ├── ro.json ├── ru.js ├── ru.json ├── sc.js ├── sc.json ├── sk.js ├── sk.json ├── sl.js ├── sl.json ├── sq.js ├── sq.json ├── sr.js ├── sr.json ├── sv.js ├── sv.json ├── th.js ├── th.json ├── tr.js ├── tr.json ├── ug.js ├── ug.json ├── uk.js ├── uk.json ├── uz.js ├── uz.json ├── vi.js ├── vi.json ├── zh_CN.js ├── zh_CN.json ├── zh_HK.js ├── zh_HK.json ├── zh_TW.js └── zh_TW.json ├── lib ├── Activity │ └── PollChanges.php ├── AppConstants.php ├── AppInfo │ └── Application.php ├── Attributes │ └── ShareTokenRequired.php ├── Command │ ├── Command.php │ ├── Db │ │ ├── CleanMigrations.php │ │ ├── CreateIndices.php │ │ ├── Purge.php │ │ ├── Rebuild.php │ │ ├── RemoveIndices.php │ │ └── ResetWatch.php │ ├── Poll │ │ └── TransferOwnership.php │ └── Share │ │ ├── Add.php │ │ ├── Remove.php │ │ └── TShareCommand.php ├── Controller │ ├── AdminController.php │ ├── BaseApiV2Controller.php │ ├── BaseController.php │ ├── CommentApiController.php │ ├── CommentController.php │ ├── OptionApiController.php │ ├── OptionController.php │ ├── PageController.php │ ├── PollApiController.php │ ├── PollController.php │ ├── PublicController.php │ ├── SettingsController.php │ ├── ShareApiController.php │ ├── ShareController.php │ ├── SubscriptionApiController.php │ ├── SubscriptionController.php │ ├── SystemController.php │ ├── UserApiController.php │ ├── UserController.php │ ├── VoteApiController.php │ ├── VoteController.php │ └── WatchController.php ├── Cron │ ├── AutoReminderCron.php │ ├── GroupDeletedJob.php │ ├── JanitorCron.php │ ├── NotificationCron.php │ └── UserDeletedJob.php ├── Dashboard │ └── PollWidget.php ├── Db │ ├── Comment.php │ ├── CommentMapper.php │ ├── EntityWithUser.php │ ├── IndexManager.php │ ├── Log.php │ ├── LogMapper.php │ ├── Model.php │ ├── Option.php │ ├── OptionMapper.php │ ├── Poll.php │ ├── PollMapper.php │ ├── Preferences.php │ ├── PreferencesMapper.php │ ├── QBMapperWithUser.php │ ├── Share.php │ ├── ShareMapper.php │ ├── Subscription.php │ ├── SubscriptionMapper.php │ ├── TableManager.php │ ├── UserMapper.php │ ├── Vote.php │ ├── VoteMapper.php │ ├── Watch.php │ └── WatchMapper.php ├── Event │ ├── BaseEvent.php │ ├── CommentAddEvent.php │ ├── CommentDeleteEvent.php │ ├── CommentEvent.php │ ├── OptionConfirmedEvent.php │ ├── OptionCreatedEvent.php │ ├── OptionDeletedEvent.php │ ├── OptionEvent.php │ ├── OptionUnconfirmedEvent.php │ ├── OptionUpdatedEvent.php │ ├── PollArchivedEvent.php │ ├── PollCloseEvent.php │ ├── PollCreatedEvent.php │ ├── PollDeletedEvent.php │ ├── PollEvent.php │ ├── PollExpiredEvent.php │ ├── PollOptionReorderedEvent.php │ ├── PollOwnerChangeEvent.php │ ├── PollReopenEvent.php │ ├── PollRestoredEvent.php │ ├── PollUpdatedEvent.php │ ├── ShareChangedDisplayNameEvent.php │ ├── ShareChangedEmailEvent.php │ ├── ShareChangedLabelEvent.php │ ├── ShareChangedRegistrationConstraintEvent.php │ ├── ShareCreateEvent.php │ ├── ShareDeletedEvent.php │ ├── ShareEvent.php │ ├── ShareLockedEvent.php │ ├── ShareRegistrationEvent.php │ ├── ShareTypeChangedEvent.php │ ├── VoteEvent.php │ └── VoteSetEvent.php ├── Exceptions │ ├── AlreadyDeletedException.php │ ├── CirclesNotEnabledException.php │ ├── ContactsNotEnabledExceptions.php │ ├── EmptyTitleException.php │ ├── Exception.php │ ├── ForbiddenException.php │ ├── InsufficientAttributesException.php │ ├── InvalidAccessException.php │ ├── InvalidClassException.php │ ├── InvalidEmailAddress.php │ ├── InvalidPollTypeException.php │ ├── InvalidShareTypeException.php │ ├── InvalidShowResultsException.php │ ├── InvalidUsernameException.php │ ├── NoDeadLineException.php │ ├── NoEmailAddress.php │ ├── NoUpdatesException.php │ ├── NotAuthorizedException.php │ ├── NotFoundException.php │ ├── OCPEventException.php │ ├── ShareAlreadyExistsException.php │ ├── ShareNotFoundException.php │ ├── TooShortException.php │ ├── UserNotFoundException.php │ └── VoteLimitExceededException.php ├── Filter │ └── ActivityFilter.php ├── Helper │ └── Container.php ├── Listener │ ├── BaseListener.php │ ├── CommentListener.php │ ├── GroupDeletedListener.php │ ├── OptionListener.php │ ├── PollListener.php │ ├── PollsReferenceListener.php │ ├── ShareListener.php │ ├── UserDeletedListener.php │ └── VoteListener.php ├── Middleware │ └── RequestAttributesMiddleware.php ├── Migration │ ├── FixVotes.php │ ├── RepairSteps │ │ ├── CreateIndices.php │ │ ├── CreateTables.php │ │ ├── DeleteInvalidRecords.php │ │ ├── DropOrphanedColumns.php │ │ ├── DropOrphanedTables.php │ │ ├── Install.php │ │ ├── RemoveIndices.php │ │ ├── RemoveObsoleteMigrations.php │ │ ├── UpdateHashes.php │ │ └── UpdateInteraction.php │ ├── TableSchema.php │ ├── Version060100Date20240209073304.php │ └── Version080000Date20250413195001.php ├── Model │ ├── CalendarEvent.php │ ├── Group │ │ ├── Circle.php │ │ ├── ContactGroup.php │ │ └── Group.php │ ├── Mail │ │ ├── ConfirmationMail.php │ │ ├── InvitationMail.php │ │ ├── MailBase.php │ │ ├── NotificationMail.php │ │ └── ReminderMail.php │ ├── Search │ │ └── PollsSearchResultEntry.php │ ├── SentResult.php │ ├── Sequence.php │ ├── SequenceUnit.php │ ├── Settings │ │ ├── AppSettings.php │ │ └── SystemSettings.php │ ├── SimpleOption.php │ ├── User │ │ ├── Admin.php │ │ ├── Anon.php │ │ ├── Contact.php │ │ ├── Cron.php │ │ ├── Email.php │ │ ├── GenericUser.php │ │ ├── Ghost.php │ │ └── User.php │ └── UserBase.php ├── Notification │ └── Notifier.php ├── Provider │ ├── ActivityProvider.php │ ├── ReferenceProvider.php │ └── SearchProvider.php ├── ResponseDefinitions.php ├── Service │ ├── ActivityService.php │ ├── CalendarService.php │ ├── CommentService.php │ ├── LogService.php │ ├── MailService.php │ ├── NotificationService.php │ ├── OptionService.php │ ├── PollService.php │ ├── PreferencesService.php │ ├── SettingsService.php │ ├── ShareService.php │ ├── SubscriptionService.php │ ├── SystemService.php │ ├── VoteService.php │ └── WatchService.php ├── Settings │ ├── ActivitySettings.php │ ├── ActivityVote.php │ ├── AdminSection.php │ ├── AdminSettings.php │ ├── PersonalSection.php │ └── PersonalSettings.php ├── Types.php └── UserSession.php ├── package-lock.json ├── package.json ├── phpunit.integration.xml ├── psalm-baseline.xml ├── psalm-baseline.xml.license ├── psalm.xml ├── screenshots ├── edit-poll.png ├── overview.png ├── share.png └── vote.png ├── src ├── Api │ ├── index.ts │ └── modules │ │ ├── HttpApi.js │ │ ├── activity.ts │ │ ├── admin.ts │ │ ├── appSettings.ts │ │ ├── calendar.ts │ │ ├── comments.ts │ │ ├── options.ts │ │ ├── polls.ts │ │ ├── public.ts │ │ ├── session.ts │ │ ├── shares.ts │ │ ├── userSettings.ts │ │ ├── validators.ts │ │ └── votes.ts ├── App.vue ├── Exceptions │ └── Exceptions.ts ├── Types │ └── index.ts ├── adminSettings.ts ├── assets │ ├── icons │ │ ├── maybe-vote.svg │ │ ├── no-vote.svg │ │ ├── spinner.svg │ │ └── yes-vote.svg │ └── scss │ │ ├── colors.scss │ │ ├── hacks.scss │ │ ├── markdown.scss │ │ ├── polls-icon.scss │ │ ├── print.scss │ │ └── transitions.scss ├── components │ ├── Actions │ │ ├── index.ts │ │ └── modules │ │ │ ├── ActionAddOption.vue │ │ │ ├── ActionAddPoll.vue │ │ │ ├── ActionDelete.vue │ │ │ ├── ActionDeleteOrphanedVotes.vue │ │ │ ├── ActionOpenOptionsSidebar.vue │ │ │ ├── ActionOpenSharesSidebar.vue │ │ │ ├── ActionRegister.vue │ │ │ ├── ActionSendConfirmed.vue │ │ │ ├── ActionSwitchSafeTable.vue │ │ │ └── ActionToggleSidebar.vue │ ├── Activity │ │ ├── Activities.vue │ │ └── ActivityItem.vue │ ├── AppIcons │ │ ├── index.ts │ │ └── modules │ │ │ ├── MaybeIcon.vue │ │ │ ├── PollsAppIcon.vue │ │ │ ├── Spinner.vue │ │ │ └── types.ts │ ├── Base │ │ ├── index.ts │ │ └── modules │ │ │ ├── BadgeDiv.vue │ │ │ ├── BadgeSmallDiv.vue │ │ │ ├── ButtonModal.vue │ │ │ ├── CardDiv.vue │ │ │ ├── Collapsible.vue │ │ │ ├── ConfigBox.vue │ │ │ ├── DateBox.vue │ │ │ ├── DateTimePicker.vue │ │ │ ├── FlexSettings.vue │ │ │ ├── FlexSpacer.vue │ │ │ ├── HeaderBar.vue │ │ │ ├── InputDiv.vue │ │ │ ├── IntersectionObserver.vue │ │ │ ├── LoadingOverlay.vue │ │ │ ├── QrModal.vue │ │ │ └── RadioGroupDiv.vue │ ├── Calendar │ │ ├── CalendarInfo.vue │ │ └── CalendarPeek.vue │ ├── Cards │ │ ├── VoteInfoCards.vue │ │ ├── index.ts │ │ └── modules │ │ │ ├── CardAddProposals.vue │ │ │ ├── CardAnonymousPollHint.vue │ │ │ ├── CardClosedPoll.vue │ │ │ ├── CardHiddenParticipants.vue │ │ │ ├── CardLimitedVotes.vue │ │ │ ├── CardLocked.vue │ │ │ ├── CardRegister.vue │ │ │ ├── CardSendConfirmations.vue │ │ │ └── CardUnpublishedPoll.vue │ ├── Combo │ │ ├── ComboTable.vue │ │ ├── VoteColumn.vue │ │ └── VoteItem.vue │ ├── Comments │ │ ├── CommentAdd.vue │ │ ├── CommentItem.vue │ │ └── Comments.vue │ ├── Configuration │ │ ├── AutoReminderInformation.vue │ │ ├── ConfigAllowComment.vue │ │ ├── ConfigAllowMayBe.vue │ │ ├── ConfigAnonymous.vue │ │ ├── ConfigAutoReminder.vue │ │ ├── ConfigClosing.vue │ │ ├── ConfigDangerArea.vue │ │ ├── ConfigDescription.vue │ │ ├── ConfigOptionLimit.vue │ │ ├── ConfigProposals.vue │ │ ├── ConfigShowResults.vue │ │ ├── ConfigTitle.vue │ │ ├── ConfigUseNo.vue │ │ └── ConfigVoteLimit.vue │ ├── Create │ │ ├── CreateDlg.vue │ │ └── PollCreateDlg.vue │ ├── Export │ │ └── ExportPoll.vue │ ├── Modals │ │ ├── DeletePollDialog.vue │ │ ├── OptionsAddModal.vue │ │ └── TransferPollDialog.vue │ ├── Navigation │ │ └── PollNavigationItems.vue │ ├── Options │ │ ├── Counter.vue │ │ ├── OptionCloneDate.vue │ │ ├── OptionItem.vue │ │ ├── OptionItemDateBox.vue │ │ ├── OptionItemOwner.vue │ │ ├── OptionMenu.vue │ │ ├── OptionsDate.vue │ │ ├── OptionsDateAddDialog.vue │ │ ├── OptionsDateShift.vue │ │ ├── OptionsText.vue │ │ ├── OptionsTextAdd.vue │ │ └── OptionsTextAddBulk.vue │ ├── Poll │ │ ├── MarkDownDescription.vue │ │ ├── PollHeaderButtons.vue │ │ ├── PollInfoLine.vue │ │ └── PollInformation.vue │ ├── PollList │ │ ├── PollItem.vue │ │ ├── PollItemActions.vue │ │ └── PollListSort.vue │ ├── Public │ │ └── PublicRegisterModal.vue │ ├── Settings │ │ ├── AdminSettings │ │ │ ├── AdminActivities.vue │ │ │ ├── AdminArchivePolls.vue │ │ │ ├── AdminCombo.vue │ │ │ ├── AdminDeletePolls.vue │ │ │ ├── AdminEmail.vue │ │ │ ├── AdminJobs.vue │ │ │ ├── AdminLegal.vue │ │ │ ├── AdminPerformance.vue │ │ │ ├── AdminPollCreation.vue │ │ │ ├── AdminPollDownload.vue │ │ │ ├── AdminPollsInNavigation.vue │ │ │ ├── AdminShareOpenPoll.vue │ │ │ ├── AdminSharePublicCreate.vue │ │ │ ├── AdminSharePublicShowLogin.vue │ │ │ ├── AdminShowMailAddresses.vue │ │ │ ├── AdminUnrescrictedOwners.vue │ │ │ └── index.ts │ │ ├── UserSettings │ │ │ ├── CalendarSettings.vue │ │ │ ├── FeatureSettings.vue │ │ │ ├── PerformanceSettings.vue │ │ │ ├── StyleSettings.vue │ │ │ └── index.ts │ │ └── UserSettingsDlg.vue │ ├── Shares │ │ ├── ShareItem.vue │ │ ├── ShareItemAllUsers.vue │ │ ├── SharePublicAdd.vue │ │ ├── SharesList.vue │ │ ├── SharesListLocked.vue │ │ └── SharesListUnsent.vue │ ├── SideBar │ │ ├── SideBarTabActivity.vue │ │ ├── SideBarTabComments.vue │ │ ├── SideBarTabConfiguration.vue │ │ ├── SideBarTabDatePolls.vue │ │ ├── SideBarTabOptions.vue │ │ ├── SideBarTabShare.vue │ │ └── index.ts │ ├── Subscription │ │ ├── ActionSubscription.vue │ │ └── Subscription.vue │ ├── User │ │ ├── ActionInputDisplayName.vue │ │ ├── ActionInputEmailAddress.vue │ │ ├── UserItem.vue │ │ ├── UserMenu.vue │ │ └── UserSearch.vue │ └── VoteTable │ │ ├── VoteColumn.vue │ │ ├── VoteIndicator.vue │ │ ├── VoteItem.vue │ │ ├── VoteMenu.vue │ │ ├── VoteParticipant.vue │ │ └── VoteTable.vue ├── composables │ ├── context.ts │ ├── elementWidth.ts │ ├── handleScroll.ts │ ├── usePollWatcher.ts │ └── usePollWatcher.types.ts ├── constants │ └── dateUnits.ts ├── dashboard.ts ├── event-bus.d.ts ├── helpers │ ├── index.ts │ └── modules │ │ ├── GuestBubble.ts │ │ ├── SimpleLink.ts │ │ ├── StoreHelper.ts │ │ ├── arrayHelper.ts │ │ ├── comments.ts │ │ ├── cookieHelper.ts │ │ └── logger.ts ├── main.ts ├── plugins │ └── piniaDebounce.ts ├── polls-reference.ts ├── router.ts ├── stores │ ├── activity.ts │ ├── appSettings.ts │ ├── combo.ts │ ├── comments.ts │ ├── index.ts │ ├── options.ts │ ├── poll.ts │ ├── polls.ts │ ├── preferences.ts │ ├── session.ts │ ├── shares.ts │ ├── subscription.ts │ └── votes.ts ├── userSettings.ts ├── views │ ├── AdminSettingsPage.vue │ ├── Combo.vue │ ├── Dashboard.vue │ ├── Navigation.vue │ ├── NotFound.vue │ ├── PollList.vue │ ├── Reference.vue │ ├── SideBar.vue │ ├── SideBarCombo.vue │ ├── UserSettingsPage.vue │ └── Vote.vue └── workers │ └── pollWatcher.worker.ts ├── stubs └── circles-stubs.php ├── stylelint.config.cjs ├── sync_list.txt ├── sync_list.txt.license ├── templates └── main.php ├── tests ├── Integration │ ├── App │ │ └── AppTest.php │ └── Command │ │ └── Share │ │ ├── AddTest.php │ │ ├── RemoveTest.php │ │ └── TShareCommandTest.php ├── Unit │ ├── Controller │ │ └── PageControllerTest.php │ ├── Db │ │ ├── CommentMapperTest.php │ │ ├── LogMapperTest.php │ │ ├── OptionMapperTest.php │ │ ├── PollMapperTest.php │ │ ├── SubscriptionMapperTest.php │ │ └── VoteMapperTest.php │ ├── Factories │ │ ├── CommentFactory.php │ │ ├── LogFactory.php │ │ ├── OptionFactory.php │ │ ├── PollFactory.php │ │ ├── PreferencesFactory.php │ │ ├── ShareFactory.php │ │ ├── SubscriptionFactory.php │ │ └── VoteFactory.php │ ├── FactoryMuffin.php │ └── UnitTestCase.php ├── bootstrap.php ├── phpunit.integration.xml └── phpunit.xml ├── tsconfig.json ├── update-app-version.mjs ├── vendor-bin ├── openapi-extractor │ └── composer.json ├── php-cs-fixer │ └── composer.json ├── phpunit │ └── composer.json └── psalm │ └── composer.json └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | # EditorConfig is awesome: http://EditorConfig.org 4 | 5 | # top-most EditorConfig file 6 | root = true 7 | 8 | # Unix-style newlines with a newline ending every file 9 | [*] 10 | end_of_line = lf 11 | insert_final_newline = true 12 | 13 | # Set default charset 14 | charset = utf-8 15 | 16 | # 4 space tab indentation 17 | indent_style = tab 18 | indent_size = 4 19 | 20 | # Line length form NC coding guidelines 21 | trim_trailing_whitespace = true 22 | max_line_length = 80 23 | 24 | # 2 space indentation for .yml files 25 | [.*.yml] 26 | indent_style = space 27 | indent_size = 2 28 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | module.exports = { 6 | extends: ['@nextcloud/eslint-config/vue3', 'prettier'], 7 | plugins: ['promise'], 8 | rules: { 9 | 'arrow-body-style': 'error', 10 | 'jsdoc/require-jsdoc': [ 11 | 'error' | 'warn', 12 | { 13 | publicOnly: { 14 | ancestorsOnly: true, 15 | }, 16 | }, 17 | ], 18 | 'jsdoc/require-param-description': 'off', 19 | 'no-array-constructor': 'error', 20 | 'no-continue': 'error', 21 | 'no-else-return': ['error', { allowElseIf: false }], 22 | 'no-lonely-if': 'error', 23 | 'no-negated-condition': 'error', 24 | 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], 25 | 'prefer-template': 'error', 26 | 'vue/first-attribute-linebreak': [ 27 | 'error', 28 | { multiline: 'below', singleline: 'ignore' }, 29 | ], 30 | 'vue/no-v-model-argument': 'off', 31 | 'vue/no-unused-properties': [ 32 | 'error', 33 | { 34 | groups: ['props', 'data', 'computed', 'methods'], 35 | deepData: true, 36 | ignorePublicMembers: true, 37 | }, 38 | ], 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /vendor-bin/**/composer.lock binary 2 | -------------------------------------------------------------------------------- /.github/BETA_NOTIFICATION.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Polls {{ env.VERSION }} released ({{ date | date('YYYY-MM-DD') }}) 3 | labels: Announcement 4 | --- 5 | # Changelog for the upcoming release (preview) 6 | {{ env.RELEASENOTES }} 7 | 8 | # Downloads 9 | Overview and changelog: https://github.com/nextcloud/polls/releases/tag/{{ env.TAG }} 10 | Download ZIP: https://github.com/nextcloud/polls/releases/download/{{ env.TAG }}/polls-{{ env.VERSION }}.zip 11 | Download TAR.GZ: https://github.com/nextcloud/polls/releases/download/{{ env.TAG }}/polls-{{ env.VERSION }}.tar.gz 12 | -------------------------------------------------------------------------------- /.github/BETA_NOTIFICATION.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2023 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ANNOUNCEMENT.yml: -------------------------------------------------------------------------------- 1 | name: 'Announcement' 2 | description: 'Used for announcements' 3 | labels: ['announcement'] 4 | type: 'overview' 5 | 6 | body: 7 | - type: textarea 8 | attributes: 9 | label: Annoncement 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ANNOUNCEMENT.yml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.yml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: ❓ Polls Community Support and Help 4 | url: https://help.nextcloud.com/c/apps/polls/94 5 | about: Configuration or usage questions regarding Polls 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/RELEASE_NOTIFICATION.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Polls {{ env.VERSION }} released ({{ date | date('YYYY-MM-DD') }}) 3 | labels: Announcement 4 | --- 5 | # Releasenotes {{ env.VERSION }} 6 | ## [{{ env.VERSION }}] - {{ date | date('YYYY-MM-DD') }} 7 | {{ env.RELEASENOTES }} 8 | 9 | # Downloads 10 | Overview and changelog: https://github.com/nextcloud/polls/releases/tag/{{ env.TAG }} 11 | Download ZIP: https://github.com/nextcloud/polls/releases/download/{{ env.TAG }}/polls-{{ env.VERSION }}.zip 12 | Download TAR.GZ: https://github.com/nextcloud/polls/releases/download/{{ env.TAG }}/polls-{{ env.VERSION }}.tar.gz 13 | -------------------------------------------------------------------------------- /.github/RELEASE_NOTIFICATION.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2023 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/actions/get-polls-version/action.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: Read polls version from info.xml 4 | inputs: 5 | skip-check: 6 | description: Do not check tag against version 7 | required: false 8 | type: boolean 9 | default: false 10 | 11 | outputs: 12 | app-version: 13 | description: 'Version string from app' 14 | value: ${{ steps.appinfo.outputs.info }} 15 | tag-version: 16 | description: 'Version string from tag' 17 | value: ${{ steps.gettag.outputs.VERSION }} 18 | 19 | runs: 20 | using: 'composite' 21 | steps: 22 | - name: Get app version from appinfo/info.xml 23 | id: appinfo 24 | uses: mavrosxristoforos/get-xml-info@2.0 25 | with: 26 | xml-file: 'appinfo/info.xml' 27 | xpath: '//info//version' 28 | 29 | - name: Get tag name 30 | id: gettag 31 | run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_OUTPUT 32 | shell: bash 33 | 34 | - name: Compare versions 35 | if: ${{ !inputs.skip-version && format('v{0}', steps.appinfo.outputs.info) != steps.gettag.outputs.VERSION }} 36 | uses: actions/github-script@v6 37 | with: 38 | script: | 39 | core.setFailed('App version ${{ format('v{0}', steps.appinfo.outputs.info) }} is not equal to tag name ${{ steps.gettag.outputs.VERSION }}!') 40 | -------------------------------------------------------------------------------- /.github/actions/setup-node/action.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: Setup node and dependencies 4 | inputs: 5 | node-version: 6 | required: false 7 | default: 20 8 | description: 'Node version to use' 9 | 10 | outputs: 11 | cache-hit: 12 | description: 'Return cache hit' 13 | value: ${{ steps.cache-modules.outputs.cache-hit }} 14 | 15 | runs: 16 | using: 'composite' 17 | steps: 18 | - name: Use or setup caching npm modules 19 | uses: actions/cache@v4 20 | id: cache-modules 21 | env: 22 | cache-name: cache-node-modules 23 | with: 24 | path: node_modules 25 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 26 | restore-keys: | 27 | ${{ runner.os }}-build-${{ env.cache-name }}- 28 | ${{ runner.os }}-build- 29 | ${{ runner.os }}- 30 | 31 | - name: Set up node ${{ inputs.node-version }} 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: ${{ inputs.node-version }} 35 | 36 | - name: Install dependencies 37 | if: steps.cache-modules.outputs.cache-hit != 'true' 38 | run: npm ci 39 | shell: bash 40 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes 2 | # SPDX-FileCopyrightText: 2023 Nextcloud contributors 3 | # SPDX-License-Identifier: AGPL-3.0-or-later 4 | changelog: 5 | exclude: 6 | authors: 7 | - dependabot 8 | - dependabot[bot] 9 | - nextcloud-bot 10 | - nextcloud-command 11 | labels: 12 | - automated 13 | - dependencies 14 | - duplicate 15 | - invalid 16 | - l10n 17 | - question 18 | - wontfix 19 | 20 | categories: 21 | - title: 🔐 Security 22 | labels: 23 | - security 24 | - title: 💥 Breaking Changes 25 | labels: 26 | - breaking 27 | - title: 🚀 Enhancements 28 | labels: 29 | - enhancements 30 | - enhancement 31 | - title: 🐛 Fixed bugs 32 | labels: 33 | - bug 34 | - title: ⚡ Performance related changes 35 | labels: 36 | - performance 37 | - title: Other Changes 38 | labels: 39 | - '*' 40 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: auto-merge 4 | on: pull_request_target 5 | 6 | jobs: 7 | auto-merge: 8 | runs-on: ubuntu-latest 9 | steps: 10 | # Default github action approve 11 | - uses: hmarr/auto-approve-action@v4 12 | if: github.ref == 'refs/heads/master' && 13 | (github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]') 14 | with: 15 | github-token: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | # Nextcloud bot approve and merge request 18 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 19 | if: github.ref == 'refs/heads/master' && 20 | (github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]') 21 | with: 22 | target: minor 23 | github-token: ${{ secrets.DEPENDABOT_AUTOMERGE_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: "CodeQL" 4 | 5 | on: 6 | push: 7 | branches: [ "main", "master*", "next", "stable-*" ] 8 | pull_request: 9 | branches: [ "main", "master*", "next" ] 10 | schedule: 11 | - cron: '26 15 * * 4' 12 | 13 | jobs: 14 | analyze: 15 | runs-on: ubuntu-latest 16 | 17 | name: Analyze 18 | 19 | permissions: 20 | actions: read 21 | contents: read 22 | security-events: write 23 | 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | language: [ 'javascript' ] 28 | 29 | steps: 30 | - name: Checkout repository 31 | uses: actions/checkout@v4 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v3 34 | with: 35 | languages: ${{ matrix.language }} 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v3 40 | -------------------------------------------------------------------------------- /.github/workflows/lint-info-xml.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Lint info.xml 10 | 11 | on: pull_request 12 | 13 | permissions: 14 | contents: read 15 | 16 | concurrency: 17 | group: lint-info-xml-${{ github.head_ref || github.run_id }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | xml-linters: 22 | runs-on: ubuntu-latest-low 23 | 24 | name: info.xml lint 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 28 | with: 29 | persist-credentials: false 30 | 31 | - name: Download schema 32 | run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd 33 | 34 | - name: Lint info.xml 35 | uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2 36 | with: 37 | xml-file: ./appinfo/info.xml 38 | xml-schema-file: ./info.xsd 39 | -------------------------------------------------------------------------------- /.github/workflows/lock-closed-issues.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: 'Lock Issues' 4 | 5 | on: 6 | schedule: 7 | - cron: '2 4 * * *' 8 | 9 | concurrency: lock_issues 10 | 11 | jobs: 12 | lock: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: dessant/lock-threads@v5 16 | with: 17 | github-token: ${{ github.token }} 18 | issue-inactive-days: '31' 19 | exclude-issue-created-before: '' 20 | exclude-any-issue-labels: '' 21 | add-issue-labels: '' 22 | issue-comment: > 23 | This thread has been automatically locked since there has not been 24 | any recent activity after it was closed. Please open a new issue for 25 | related bugs. 26 | issue-lock-reason: 'resolved' 27 | process-only: 'issues' 28 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: Node CI 4 | 5 | on: 6 | pull_request: 7 | push: 8 | branches: 9 | - main 10 | - master* 11 | - next 12 | - stable* 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | name: Build app 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup node 23 | uses: ./.github/actions/setup-node 24 | with: 25 | node-version: '20' 26 | 27 | - name: build 28 | run: npm run build --if-present 29 | -------------------------------------------------------------------------------- /.github/workflows/reuse.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | # SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. 7 | # 8 | # SPDX-License-Identifier: CC0-1.0 9 | 10 | name: REUSE Compliance Check 11 | 12 | on: [pull_request] 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | reuse-compliance-check: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | with: 24 | persist-credentials: false 25 | 26 | - name: REUSE Compliance Check 27 | uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5.0.0 28 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | name: Mark stale issues 4 | 5 | on: 6 | schedule: 7 | - cron: "30 1 * * *" 8 | 9 | jobs: 10 | stale: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/stale@v9 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | stale-issue-message: 'This issue is marked as stale, because it had no activity in the last 30 days. It will be closed in 5 days.' 17 | stale-issue-label: 'stale' 18 | stale-pr-label: 'stale' 19 | exempt-issue-labels: 'enhancement,bug,investigate,UX' 20 | exempt-pr-labels: 'enhancement,bug,investigate,UX' 21 | exempt-all-milestones: true 22 | days-before-stale: 30 23 | days-before-close: 5 24 | days-before-issue-stale: 30 25 | days-before-issue-close: 5 26 | days-before-pr-stale: -1 27 | days-before-pr-close: -1 28 | exempt-draft-pr: true 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2016 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | .DS_Store 4 | .sass-cache/ 5 | .php_cs.cache 6 | .php-cs-fixer.cache 7 | .psalm.cache 8 | .project/ 9 | .idea/ 10 | .vscode/ 11 | assets/ 12 | build/ 13 | css/*.map 14 | js/* 15 | css/* 16 | nbproject/ 17 | node_modules/ 18 | tests/.phpunit.result.cache 19 | npm-debug.log 20 | vendor 21 | vendor-bin/**/vendor/ 22 | vendor-bin/**/*.lock 23 | update-workflows.sh 24 | yarn-error.log 25 | Thumbs.db 26 | *.cmd 27 | *.env 28 | *.iml 29 | *.ntvs* 30 | *.njsproj 31 | *.sln 32 | *.suo 33 | tests/Api 34 | -------------------------------------------------------------------------------- /.l10nignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | # compiled vue templates 4 | js/* 5 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | getFinder() 15 | ->ignoreVCSIgnored(true) 16 | ->notPath('build') 17 | ->notPath('l10n') 18 | ->notPath('lib/Vendor') 19 | ->notPath('src') 20 | ->notPath('vendor') 21 | ->notPath('tests') 22 | ->in(__DIR__); 23 | return $config; 24 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | # version control systems directories 4 | **/.git 5 | **/.svn 6 | **/.hg 7 | 8 | # Github workflows 9 | .github/workflows/ 10 | 11 | # 3rdparty dependencies 12 | **/node_modules 13 | **/vendor 14 | **/vendor-bin 15 | 16 | # Compiled JS output 17 | js/ 18 | 19 | # Handled by transifex 20 | l10n/ 21 | 22 | # OpenAPI 23 | openapi.json 24 | 25 | # PHP 26 | appinfo/ 27 | lib/ 28 | tests/ 29 | templates/ 30 | **/*.php 31 | composer.json 32 | composer.lock 33 | 34 | # individual files 35 | screenshots/ 36 | img/ 37 | cache/ 38 | build/ 39 | *.md 40 | 41 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | "@nextcloud/prettier-config" 2 | -------------------------------------------------------------------------------- /.prettierrc.json.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2024 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | assets/**/* 4 | js/**/* 5 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | lang_map = sk_SK: sk, th_TH: th, ja_JP: ja, bg_BG: bg, cs_CZ: cs, fi_FI: fi, hu_HU: hu, nb_NO: nb 4 | 5 | [o:nextcloud:p:nextcloud:r:polls] 6 | file_filter = translationfiles//polls.po 7 | source_file = translationfiles/templates/polls.pot 8 | source_lang = en 9 | type = PO 10 | 11 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | 5 | # Authors 6 | 7 | - Bernhard Posselt 8 | - Daniel Rudolf 9 | - Jonas Rittershofer 10 | - Julius Härtl 11 | - Kai Schröer 12 | - Michael Longo 13 | - René Gieling 14 | - Vinzenz Rosenkranz 15 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | theme: jekyll-theme-slate 4 | -------------------------------------------------------------------------------- /appinfo/info.xml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /appinfo/routes.php: -------------------------------------------------------------------------------- 1 | [ 10 | // REST-API calls 11 | ['name' => 'baseApiV1#preflighted_cors', 'url' => '/api/v1.0/{path}', 'verb' => 'OPTIONS', 'requirements' => ['path' => '.+']], 12 | ], 13 | // 'ocs' => [ 14 | // // CORS Preflight 15 | // ['name' => 'api#preflightedCors', 'url' => $apiBase . '{path}', 'verb' => 'OPTIONS', 'requirements' => [ 16 | // 'path' => '.+', 17 | // 'apiVersion' => 'v2' 18 | // ]], 19 | // ], 20 | ]; 21 | -------------------------------------------------------------------------------- /external.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2025 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | declare module 'vue-material-design-icons/*' 7 | declare module 'v-click-outside' 8 | // TODO: remove this when the package is fixed 9 | declare module '@nextcloud/dialogs' 10 | -------------------------------------------------------------------------------- /img/app.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /img/polls-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/polls.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6" 5 | }, 6 | "exclude": ["node_modules"], 7 | "include": ["src/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /l10n/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/polls/af1a3ad27348db5bf0bae9ba381cd4a89411c47e/l10n/.gitkeep -------------------------------------------------------------------------------- /l10n/af.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "polls", 3 | { 4 | "Group" : "Groep", 5 | "Legal Notice" : "Regskennisgewing", 6 | "Contact" : "Kontak", 7 | "User" : "Gebruiker", 8 | "Delete" : "Skrap", 9 | "Register" : "Registreer", 10 | "Unknown error" : "Onbekende fout", 11 | "Add" : "Voeg by", 12 | "New comment …" : "Nuwe kommentaar…", 13 | "Delete comment" : "Skrap kommentaar", 14 | "Cancel" : "Kanselleer", 15 | "Title" : "Titel", 16 | "Close" : "Sluit", 17 | "Yes" : "Ja", 18 | "No" : "Nee", 19 | "From" : "Van", 20 | "To" : "Aan", 21 | "Email address" : "E-posadres", 22 | "OK" : "Goed", 23 | "All day" : "Heeldag", 24 | "Preview" : "Voorskou", 25 | "Submit" : "Dien in", 26 | "never" : "nooit", 27 | "Expiration" : "Verval", 28 | "Privacy policy" : "Privaatheidsbeleid", 29 | "Login" : "Teken aan", 30 | "No activity yet" : "Nog geen aktiwiteit", 31 | "Description" : "Beskrywing", 32 | "Public link" : "Openbare skakel", 33 | "Comments" : "Kommentare", 34 | "Details" : "Besonderhede", 35 | "Activity" : "Aktiwiteit", 36 | "Hour" : "Uur", 37 | "Day" : "Dag", 38 | "Week" : "Week", 39 | "Month" : "Maand", 40 | "Created" : "Geskep", 41 | "Owner" : "Eienaar", 42 | "Archive" : "Argief" 43 | }, 44 | "nplurals=2; plural=(n != 1);"); 45 | -------------------------------------------------------------------------------- /l10n/af.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Group" : "Groep", 3 | "Legal Notice" : "Regskennisgewing", 4 | "Contact" : "Kontak", 5 | "User" : "Gebruiker", 6 | "Delete" : "Skrap", 7 | "Register" : "Registreer", 8 | "Unknown error" : "Onbekende fout", 9 | "Add" : "Voeg by", 10 | "New comment …" : "Nuwe kommentaar…", 11 | "Delete comment" : "Skrap kommentaar", 12 | "Cancel" : "Kanselleer", 13 | "Title" : "Titel", 14 | "Close" : "Sluit", 15 | "Yes" : "Ja", 16 | "No" : "Nee", 17 | "From" : "Van", 18 | "To" : "Aan", 19 | "Email address" : "E-posadres", 20 | "OK" : "Goed", 21 | "All day" : "Heeldag", 22 | "Preview" : "Voorskou", 23 | "Submit" : "Dien in", 24 | "never" : "nooit", 25 | "Expiration" : "Verval", 26 | "Privacy policy" : "Privaatheidsbeleid", 27 | "Login" : "Teken aan", 28 | "No activity yet" : "Nog geen aktiwiteit", 29 | "Description" : "Beskrywing", 30 | "Public link" : "Openbare skakel", 31 | "Comments" : "Kommentare", 32 | "Details" : "Besonderhede", 33 | "Activity" : "Aktiwiteit", 34 | "Hour" : "Uur", 35 | "Day" : "Dag", 36 | "Week" : "Week", 37 | "Month" : "Maand", 38 | "Created" : "Geskep", 39 | "Owner" : "Eienaar", 40 | "Archive" : "Argief" 41 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 42 | } -------------------------------------------------------------------------------- /l10n/nn_NO.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "polls", 3 | { 4 | "Group" : "Gruppe", 5 | "Contact" : "Kontakt", 6 | "User" : "Bruker", 7 | "Delete" : "Slett", 8 | "Unknown error" : "Ukjend feil", 9 | "Add" : "Legg til", 10 | "New comment …" : "Ny kommentar...", 11 | "Delete comment" : "Slett kommentar", 12 | "Cancel" : "Avbryt", 13 | "Ok" : "Greitt", 14 | "Title" : "Tittel", 15 | "Close" : "Lukk", 16 | "Yes" : "Ja", 17 | "No" : "Nei", 18 | "Participants" : "Deltakarar", 19 | "OK" : "OK", 20 | "Sort" : "Sorter", 21 | "never" : "aldri", 22 | "Expiration" : "Utløp", 23 | "Login" : "Login", 24 | "Styles" : "Stilar", 25 | "Shares" : "Delingar", 26 | "No activity yet" : "Ingen aktivitetar enno", 27 | "Description" : "Skildring", 28 | "Other settings" : "Andre innstillingar", 29 | "Comments" : "Kommentarar", 30 | "Details" : "Detaljar", 31 | "Configuration" : "Innstillingar", 32 | "Sharing" : "Deling", 33 | "Activity" : "Aktivitet", 34 | "Minute" : "Minutt", 35 | "Day" : "Dag", 36 | "Week" : "Veke", 37 | "Month" : "Månad", 38 | "Year" : "År", 39 | "Created" : "Lagd", 40 | "Owner" : "Owner", 41 | "Administration" : "Administrasjon" 42 | }, 43 | "nplurals=2; plural=(n != 1);"); 44 | -------------------------------------------------------------------------------- /l10n/nn_NO.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Group" : "Gruppe", 3 | "Contact" : "Kontakt", 4 | "User" : "Bruker", 5 | "Delete" : "Slett", 6 | "Unknown error" : "Ukjend feil", 7 | "Add" : "Legg til", 8 | "New comment …" : "Ny kommentar...", 9 | "Delete comment" : "Slett kommentar", 10 | "Cancel" : "Avbryt", 11 | "Ok" : "Greitt", 12 | "Title" : "Tittel", 13 | "Close" : "Lukk", 14 | "Yes" : "Ja", 15 | "No" : "Nei", 16 | "Participants" : "Deltakarar", 17 | "OK" : "OK", 18 | "Sort" : "Sorter", 19 | "never" : "aldri", 20 | "Expiration" : "Utløp", 21 | "Login" : "Login", 22 | "Styles" : "Stilar", 23 | "Shares" : "Delingar", 24 | "No activity yet" : "Ingen aktivitetar enno", 25 | "Description" : "Skildring", 26 | "Other settings" : "Andre innstillingar", 27 | "Comments" : "Kommentarar", 28 | "Details" : "Detaljar", 29 | "Configuration" : "Innstillingar", 30 | "Sharing" : "Deling", 31 | "Activity" : "Aktivitet", 32 | "Minute" : "Minutt", 33 | "Day" : "Dag", 34 | "Week" : "Veke", 35 | "Month" : "Månad", 36 | "Year" : "År", 37 | "Created" : "Lagd", 38 | "Owner" : "Owner", 39 | "Administration" : "Administrasjon" 40 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 41 | } -------------------------------------------------------------------------------- /l10n/th.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "polls", 3 | { 4 | "Group" : "กลุ่ม", 5 | "User" : "ผู้ใช้", 6 | "Delete" : "ลบ", 7 | "Register" : "ลงทะเบียน", 8 | "Unknown error" : "ข้อผิดพลาดที่ไม่รู้จัก", 9 | "Loading" : "กำลังโหลด", 10 | "Add" : "เพิ่ม", 11 | "New comment …" : "ความคิดเห็นใหม่ ...", 12 | "Delete comment" : "ลบความคิดเห็น", 13 | "Cancel" : "ยกเลิก", 14 | "Ok" : "ตกลง", 15 | "Title" : "ชื่อเรื่อง", 16 | "Close" : "ปิด", 17 | "Yes" : "ใช่", 18 | "No" : "ไม่ใช่", 19 | "Email address" : "ที่อยู่อีเมล", 20 | "OK" : "ตกลง", 21 | "Sort" : "จัดเรียง", 22 | "Preview" : "ตัวอย่าง", 23 | "Submit" : "ส่ง", 24 | "never" : "ไม่ต้องเลย", 25 | "Expiration" : "การหมดอายุ", 26 | "Login" : "เข้าสู่ระบบ", 27 | "Shares" : "การแชร์", 28 | "No activity yet" : "ยังไม่มีกิจกรรม", 29 | "Description" : "รายละเอียด", 30 | "Change name" : "เปลี่ยนชื่อ", 31 | "Public link" : "ลิงก์สาธารณะ", 32 | "Email options" : "ตัวเลือกอีเมล", 33 | "Comments" : "ความคิดเห็น", 34 | "Details" : "รายละเอียด", 35 | "Configuration" : "การกำหนดค่า", 36 | "Options" : "ตัวเลือก", 37 | "Sharing" : "การแชร์", 38 | "Activity" : "กิจกรรม", 39 | "Day" : "วัน", 40 | "Week" : "สัปดาห์", 41 | "Month" : "เดือน", 42 | "Year" : "ปี", 43 | "Owner" : "เจ้าของ", 44 | "Archive" : "เก็บถาวร" 45 | }, 46 | "nplurals=1; plural=0;"); 47 | -------------------------------------------------------------------------------- /l10n/th.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Group" : "กลุ่ม", 3 | "User" : "ผู้ใช้", 4 | "Delete" : "ลบ", 5 | "Register" : "ลงทะเบียน", 6 | "Unknown error" : "ข้อผิดพลาดที่ไม่รู้จัก", 7 | "Loading" : "กำลังโหลด", 8 | "Add" : "เพิ่ม", 9 | "New comment …" : "ความคิดเห็นใหม่ ...", 10 | "Delete comment" : "ลบความคิดเห็น", 11 | "Cancel" : "ยกเลิก", 12 | "Ok" : "ตกลง", 13 | "Title" : "ชื่อเรื่อง", 14 | "Close" : "ปิด", 15 | "Yes" : "ใช่", 16 | "No" : "ไม่ใช่", 17 | "Email address" : "ที่อยู่อีเมล", 18 | "OK" : "ตกลง", 19 | "Sort" : "จัดเรียง", 20 | "Preview" : "ตัวอย่าง", 21 | "Submit" : "ส่ง", 22 | "never" : "ไม่ต้องเลย", 23 | "Expiration" : "การหมดอายุ", 24 | "Login" : "เข้าสู่ระบบ", 25 | "Shares" : "การแชร์", 26 | "No activity yet" : "ยังไม่มีกิจกรรม", 27 | "Description" : "รายละเอียด", 28 | "Change name" : "เปลี่ยนชื่อ", 29 | "Public link" : "ลิงก์สาธารณะ", 30 | "Email options" : "ตัวเลือกอีเมล", 31 | "Comments" : "ความคิดเห็น", 32 | "Details" : "รายละเอียด", 33 | "Configuration" : "การกำหนดค่า", 34 | "Options" : "ตัวเลือก", 35 | "Sharing" : "การแชร์", 36 | "Activity" : "กิจกรรม", 37 | "Day" : "วัน", 38 | "Week" : "สัปดาห์", 39 | "Month" : "เดือน", 40 | "Year" : "ปี", 41 | "Owner" : "เจ้าของ", 42 | "Archive" : "เก็บถาวร" 43 | },"pluralForm" :"nplurals=1; plural=0;" 44 | } -------------------------------------------------------------------------------- /lib/Activity/PollChanges.php: -------------------------------------------------------------------------------- 1 | l10n->t('Poll changes'); 31 | } 32 | 33 | public function getIcon() : string { 34 | return $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath(AppConstants::APP_ID, 'polls.svg')); 35 | } 36 | 37 | public function getPriority() : int { 38 | return 70; 39 | } 40 | 41 | public function allowedApps() : array { 42 | return [AppConstants::APP_ID]; 43 | } 44 | 45 | public function filterTypes(array $types) : array { 46 | return ['poll_add', 'vote_set']; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/AppConstants.php: -------------------------------------------------------------------------------- 1 | schema = $this->connection->createSchema(); 37 | $this->tableManager->setSchema($this->schema); 38 | $this->tableManager->removeObsoleteMigrations(); 39 | 40 | $this->printComment('Remove migration entries from migration table'); 41 | $this->connection->migrateToSchema($this->schema); 42 | 43 | return 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Command/Db/Purge.php: -------------------------------------------------------------------------------- 1 | tableManager->setConnection($this->connection); 39 | $messages = $this->tableManager->purgeTables(); 40 | $this->printInfo($messages, ' - '); 41 | return 0; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Command/Share/TShareCommand.php: -------------------------------------------------------------------------------- 1 | getUID(); 37 | }, $this->userManager->search($context->getCurrentWord())); 38 | } 39 | 40 | /** 41 | * @psalm-suppress UnusedMethod 42 | */ 43 | private function completeGroupValues(CompletionContext $context): array { 44 | return array_map(function (IGroup $group) { 45 | return $group->getGID(); 46 | }, $this->groupManager->search($context->getCurrentWord())); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Controller/WatchController.php: -------------------------------------------------------------------------------- 1 | response(fn () => ['updates' => $this->watchService->watchUpdates($pollId, $offset)]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Cron/AutoReminderCron.php: -------------------------------------------------------------------------------- 1 | session->set(AppConstants::SESSION_KEY_CRON_JOB, true); 36 | $this->mailService->sendAutoReminder(); 37 | $this->session->remove(AppConstants::SESSION_KEY_CRON_JOB); 38 | } 39 | 40 | public function manuallyRun(): string { 41 | $this->run(null); 42 | return 'AutoReminderCron manually run.'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Cron/GroupDeletedJob.php: -------------------------------------------------------------------------------- 1 | session->set(AppConstants::SESSION_KEY_CRON_JOB, true); 38 | $group = $argument['group']; 39 | $this->logger->info('Removing group shares for deleted group', [ 40 | 'group' => $group 41 | ]); 42 | 43 | $this->shareMapper->deleteByIdAndType($group, Share::TYPE_GROUP); 44 | $this->session->remove(AppConstants::SESSION_KEY_CRON_JOB); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Cron/NotificationCron.php: -------------------------------------------------------------------------------- 1 | session->set(AppConstants::SESSION_KEY_CRON_JOB, true); 36 | $this->mailService->sendNotifications(); 37 | $this->session->remove(AppConstants::SESSION_KEY_CRON_JOB); 38 | } 39 | 40 | public function manuallyRun(): string { 41 | $this->run(null); 42 | return 'NotificationCron manually run.'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Dashboard/PollWidget.php: -------------------------------------------------------------------------------- 1 | l10n->t('Polls'); 30 | } 31 | 32 | public function getOrder(): int { 33 | return 50; 34 | } 35 | 36 | public function getIconClass(): string { 37 | return 'icon-polls-dark'; 38 | } 39 | 40 | public function getUrl(): ?string { 41 | return $this->urlGenerator->linkToRouteAbsolute(AppConstants::APP_ID . '.page.indexindex'); 42 | } 43 | 44 | public function load(): void { 45 | \OCP\Util::addScript(AppConstants::APP_ID, 'polls-dashboard'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Db/Model.php: -------------------------------------------------------------------------------- 1 | attr assignment. 20 | */ 21 | public function __set(string $name, mixed $value) { 22 | $this->setter($name, [$value]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Event/CommentAddEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::ADD; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/CommentDeleteEvent.php: -------------------------------------------------------------------------------- 1 | log = false; 18 | $this->eventId = $comment->getDeleted() ? self::DELETE : self::RESTORE; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/Event/CommentEvent.php: -------------------------------------------------------------------------------- 1 | activityObjectType = 'poll'; 25 | $this->activitySubjectParams['comment'] = [ 26 | 'type' => 'highlight', 27 | 'id' => (string)$comment->getId(), 28 | 'name' => $comment->getComment(), 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Event/OptionConfirmedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CONFIRM; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/OptionCreatedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::ADD; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/OptionDeletedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = $option->getDeleted() ? self::DELETE : self::RESTORE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/OptionEvent.php: -------------------------------------------------------------------------------- 1 | activityObjectType = 'poll'; 25 | $this->activitySubjectParams['optionTitle'] = [ 26 | 'type' => 'highlight', 27 | 'id' => (string)$this->option->getId(), 28 | 'name' => $this->option->getPollOptionText(), 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Event/OptionUnconfirmedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::UNCONFIRM; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/OptionUpdatedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::UPDATE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/PollArchivedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::DELETE; 19 | } 20 | 21 | public function getNotification(): array { 22 | if ($this->getActor() === $this->getPollOwner()) { 23 | return []; 24 | } 25 | 26 | return [ 27 | 'msgId' => Notifier::NOTIFY_POLL_ARCHIVED_BY_OTHER, 28 | 'objectType' => 'poll', 29 | 'objectValue' => $this->getPollId(), 30 | 'recipient' => $this->getPollOwner(), 31 | 'actor' => $this->getActor(), 32 | 'pollTitle' => $this->getPollTitle(), 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Event/PollCloseEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CLOSE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/PollCreatedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::ADD; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/PollDeletedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::DELETE; 20 | } 21 | 22 | 23 | public function getNotification(): array { 24 | if ($this->getActor() === $this->getPollOwner()) { 25 | return []; 26 | } 27 | 28 | return [ 29 | 'msgId' => Notifier::NOTIFY_POLL_DELETED_BY_OTHER, 30 | 'objectType' => 'poll', 31 | 'objectValue' => $this->getPollId(), 32 | 'recipient' => $this->getPollOwner(), 33 | 'actor' => $this->getActor(), 34 | 'pollTitle' => $this->getPollTitle(), 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/Event/PollEvent.php: -------------------------------------------------------------------------------- 1 | activityObjectType = 'poll'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Event/PollExpiredEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::EXPIRE; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/Event/PollOptionReorderedEvent.php: -------------------------------------------------------------------------------- 1 | log = false; 18 | $this->eventId = self::OPTION_REORDER; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/Event/PollOwnerChangeEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::OWNER_CHANGE; 21 | } 22 | public function getNotification(): array { 23 | return [ 24 | 'msgId' => Notifier::NOTIFY_POLL_CHANGED_OWNER, 25 | 'objectType' => 'poll', 26 | 'objectValue' => $this->getPollId(), 27 | 'recipient' => $this->oldOwner, 28 | 'newOwner' => $this->newOwner, 29 | 'actor' => $this->getActor(), 30 | 'pollTitle' => $this->getPollTitle(), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/Event/PollReopenEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::REOPEN; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/PollRestoredEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::RESTORE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/PollUpdatedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::UPDATE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareChangedDisplayNameEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CHANGE_DISPLAY_NAME; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareChangedEmailEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CHANGE_EMAIL; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareChangedLabelEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CHANGE_LABEL; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareChangedRegistrationConstraintEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CHANGE_REG_CONSTR; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareCreateEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::ADD; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareDeletedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = $share->getDeleted() ? self::DELETE : self::RESTORE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareLockedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = $share->getLocked() ? self::UNLOCKED : self::LOCKED; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareRegistrationEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::REGISTRATION; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/ShareTypeChangedEvent.php: -------------------------------------------------------------------------------- 1 | eventId = self::CHANGE_TYPE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Event/VoteEvent.php: -------------------------------------------------------------------------------- 1 | activityObjectType = 'poll'; 23 | $this->vote = $vote; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Event/VoteSetEvent.php: -------------------------------------------------------------------------------- 1 | log = $log; 19 | $this->eventId = self::SET; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/Exceptions/AlreadyDeletedException.php: -------------------------------------------------------------------------------- 1 | status; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Exceptions/ForbiddenException.php: -------------------------------------------------------------------------------- 1 | existingShare; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Exceptions/ShareNotFoundException.php: -------------------------------------------------------------------------------- 1 | event instanceof CommentEvent)) { 20 | throw new InvalidClassException; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Listener/GroupDeletedListener.php: -------------------------------------------------------------------------------- 1 | event instanceof GroupDeletedEvent)) { 19 | throw new InvalidClassException; 20 | } 21 | throw new OCPEventException; 22 | } 23 | 24 | protected function addCronJob() : void { 25 | if (!($this->event instanceof GroupDeletedEvent)) { 26 | return; 27 | } 28 | $this->jobList->add(GroupDeletedJob::class, ['group' => $this->event->getGroup()->getGID()]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Listener/OptionListener.php: -------------------------------------------------------------------------------- 1 | event instanceof OptionEvent)) { 24 | throw new InvalidClassException; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Listener/PollListener.php: -------------------------------------------------------------------------------- 1 | event instanceof PollEvent)) { 25 | throw new InvalidClassException; 26 | } 27 | } 28 | 29 | protected function createNotification() : void { 30 | if (!($this->event instanceof PollEvent)) { 31 | return; 32 | } 33 | if (!empty($this->event->getNotification())) { 34 | $this->notificationService->createNotification($this->event->getNotification()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/Listener/PollsReferenceListener.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class PollsReferenceListener implements IEventListener { 21 | public function handle(Event $event): void { 22 | if (!$event instanceof RenderReferenceEvent) { 23 | return; 24 | } 25 | 26 | Util::addScript(Application::APP_ID, 'polls-reference'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/Listener/ShareListener.php: -------------------------------------------------------------------------------- 1 | event instanceof ShareEvent)) { 26 | throw new InvalidClassException; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Listener/UserDeletedListener.php: -------------------------------------------------------------------------------- 1 | event instanceof UserDeletedEvent)) { 19 | throw new InvalidClassException; 20 | } 21 | throw new OCPEventException; 22 | } 23 | 24 | protected function addCronJob() : void { 25 | if (!($this->event instanceof UserDeletedEvent)) { 26 | return; 27 | } 28 | $this->jobList->add(UserDeletedJob::class, ['userId' => $this->event->getUser()->getUID()]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Listener/VoteListener.php: -------------------------------------------------------------------------------- 1 | event instanceof VoteEvent)) { 23 | throw new InvalidClassException; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Migration/FixVotes.php: -------------------------------------------------------------------------------- 1 | schema = $this->connection->createSchema(); 42 | $this->tableManager->setSchema($this->schema); 43 | $this->tableManager->fixVotes(); 44 | $this->connection->migrateToSchema($this->schema); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/CreateTables.php: -------------------------------------------------------------------------------- 1 | tableManager->removeWatch(); 36 | foreach ($messages as $message) { 37 | $output->info($message); 38 | } 39 | // secure, that the schema is updated to the current status 40 | $this->schema = $this->connection->createSchema(); 41 | $this->tableManager->setSchema($this->schema); 42 | 43 | $messages = $this->tableManager->createTables(); 44 | 45 | $this->connection->migrateToSchema($this->schema); 46 | 47 | foreach ($messages as $message) { 48 | $output->info($message); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/DropOrphanedColumns.php: -------------------------------------------------------------------------------- 1 | schema = $this->connection->createSchema(); 36 | $this->tableManager->setSchema($this->schema); 37 | $messages = $this->tableManager->removeObsoleteColumns(); 38 | $this->connection->migrateToSchema($this->schema); 39 | 40 | foreach ($messages as $message) { 41 | $output->info($message); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/DropOrphanedTables.php: -------------------------------------------------------------------------------- 1 | schema = $this->connection->createSchema(); 36 | $this->tableManager->setSchema($this->schema); 37 | 38 | $messages = $this->tableManager->removeObsoleteTables(); 39 | 40 | $this->connection->migrateToSchema($this->schema); 41 | 42 | foreach ($messages as $message) { 43 | $output->info($message); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/Install.php: -------------------------------------------------------------------------------- 1 | schema = $this->connection->createSchema(); 36 | $this->indexManager->setSchema($this->schema); 37 | 38 | $messages = array_merge($messages, $this->indexManager->createForeignKeyConstraints()); 39 | $messages = array_merge($messages, $this->indexManager->createIndices()); 40 | 41 | $this->connection->migrateToSchema($this->schema); 42 | 43 | foreach ($messages as $message) { 44 | $output->info($message); 45 | } 46 | 47 | $output->info('Polls - Foreign key contraints created.'); 48 | $output->info('Polls - Indices created.'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/RemoveObsoleteMigrations.php: -------------------------------------------------------------------------------- 1 | tableManager->setConnection($this->connection); 44 | 45 | $messages = $this->tableManager->removeObsoleteMigrations(); 46 | foreach ($messages as $message) { 47 | $output->info($message); 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/UpdateHashes.php: -------------------------------------------------------------------------------- 1 | tableManager->setConnection($this->connection); 33 | 34 | $messages = $this->tableManager->migrateOptionsToHash(); 35 | foreach ($messages as $message) { 36 | $output->info($message); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/Migration/RepairSteps/UpdateInteraction.php: -------------------------------------------------------------------------------- 1 | tableManager->setConnection($this->connection); 33 | 34 | $messages = $this->tableManager->resetLastInteraction(); 35 | foreach ($messages as $message) { 36 | $output->info($message); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Model/Search/PollsSearchResultEntry.php: -------------------------------------------------------------------------------- 1 | getTitle(), $poll->getDescription(), $poll->getVoteUrl(), 'icon-polls-dark'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/Model/SentResult.php: -------------------------------------------------------------------------------- 1 | sentMails, [ 20 | 'emailAddress' => $recipient->getEmailAddress(), 21 | 'displayName' => $recipient->getDisplayName(), 22 | ]); 23 | } 24 | 25 | public function AddAbortedMail(UserBase $recipient, string $reason = self::UNHANDELED_REASON): void { 26 | array_push($this->abortedMails, [ 27 | 'emailAddress' => $recipient->getEmailAddress(), 28 | 'displayName' => $recipient->getDisplayName(), 29 | 'reason' => $reason, 30 | ]); 31 | } 32 | 33 | /** @psalm-suppress PossiblyUnusedMethod */ 34 | public function jsonSerialize(): array { 35 | return [ 36 | 'sentMails' => $this->sentMails, 37 | 'abortedMails' => $this->abortedMails, 38 | 'countSentMails' => count($this->sentMails), 39 | 'countAbortedMails' => count($this->abortedMails), 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Model/User/Admin.php: -------------------------------------------------------------------------------- 1 | getId(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/Model/User/Cron.php: -------------------------------------------------------------------------------- 1 | description = $this->l10n->t('External participant'); 29 | $this->richObjectType = UserBase::TYPE_GUEST; 30 | 31 | if ($type === UserBase::TYPE_PUBLIC) { 32 | // $this->description = $this->l10n->t('Public link'); 33 | $this->description = ''; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Model/User/Ghost.php: -------------------------------------------------------------------------------- 1 | log = new Log(); 29 | $this->log->setPollId($pollId); 30 | $this->log->setCreated(time()); 31 | $this->log->setMessageId($messageId); 32 | $this->log->setUserId($userId ?? $this->userSession->getCurrentUserId()); 33 | 34 | $this->logMapper->insert($this->log); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Settings/ActivitySettings.php: -------------------------------------------------------------------------------- 1 | l10n->t('Events happening inside of a poll'); 30 | } 31 | 32 | public function getPriority() : int { 33 | return 90; 34 | } 35 | 36 | public function canChangeStream() : bool { 37 | return true; 38 | } 39 | 40 | public function isDefaultEnabledStream() : bool { 41 | return true; 42 | } 43 | 44 | public function canChangeMail() : bool { 45 | return true; 46 | } 47 | 48 | public function isDefaultEnabledMail() : bool { 49 | return false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Settings/ActivityVote.php: -------------------------------------------------------------------------------- 1 | l10n->t('Someone voted in a poll'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Settings/AdminSection.php: -------------------------------------------------------------------------------- 1 | l10n->t('Polls'); 32 | } 33 | 34 | public function getPriority(): int { 35 | return 80; 36 | } 37 | 38 | public function getIcon(): string { 39 | return $this->urlGenerator->imagePath(AppConstants::APP_ID, 'polls-dark.svg'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Settings/AdminSettings.php: -------------------------------------------------------------------------------- 1 | l10n->t('Polls'); 32 | } 33 | 34 | public function getPriority(): int { 35 | return 80; 36 | } 37 | 38 | public function getIcon(): string { 39 | return $this->urlGenerator->imagePath(AppConstants::APP_ID, 'polls-dark.svg'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Settings/PersonalSettings.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | ./Integration 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /psalm-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /psalm-baseline.xml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /screenshots/edit-poll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/polls/af1a3ad27348db5bf0bae9ba381cd4a89411c47e/screenshots/edit-poll.png -------------------------------------------------------------------------------- /screenshots/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/polls/af1a3ad27348db5bf0bae9ba381cd4a89411c47e/screenshots/overview.png -------------------------------------------------------------------------------- /screenshots/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/polls/af1a3ad27348db5bf0bae9ba381cd4a89411c47e/screenshots/share.png -------------------------------------------------------------------------------- /screenshots/vote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/polls/af1a3ad27348db5bf0bae9ba381cd4a89411c47e/screenshots/vote.png -------------------------------------------------------------------------------- /src/Api/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | export type ApiEmailAdressList = { 7 | displayName: string 8 | emailAddress: string 9 | combined: string 10 | } 11 | 12 | export { default as ActivityAPI } from './modules/activity.ts' 13 | export { default as AdminAPI } from './modules/admin.ts' 14 | export { default as AppSettingsAPI } from './modules/appSettings.ts' 15 | export { default as CalendarAPI } from './modules/calendar.ts' 16 | export { default as CommentsAPI } from './modules/comments.ts' 17 | export { default as OptionsAPI } from './modules/options.ts' 18 | export { default as PollsAPI } from './modules/polls.ts' 19 | export { default as PublicAPI } from './modules/public.ts' 20 | export { default as SharesAPI } from './modules/shares.ts' 21 | export { default as UserSettingsAPI } from './modules/userSettings.ts' 22 | export { default as ValidatorAPI } from './modules/validators.ts' 23 | export { default as VotesAPI } from './modules/votes.ts' 24 | export { default as SessionAPI } from './modules/session.ts' 25 | -------------------------------------------------------------------------------- /src/Api/modules/activity.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { ocsInstance, createCancelTokenHandler } from './HttpApi.js' 6 | 7 | const activity = { 8 | getActivities(pollId: number) { 9 | const response = ocsInstance.request({ 10 | method: 'GET', 11 | url: 'activity/api/v2/activity/polls', 12 | params: { 13 | format: 'json', 14 | since: 0, 15 | limit: 50, 16 | object_type: 'poll', 17 | object_id: pollId, 18 | }, 19 | cancelToken: 20 | cancelTokenHandlerObject[ 21 | this.getActivities.name 22 | ].handleRequestCancellation().token, 23 | }) 24 | return response 25 | }, 26 | } 27 | 28 | const cancelTokenHandlerObject = createCancelTokenHandler(activity) 29 | 30 | export default activity 31 | -------------------------------------------------------------------------------- /src/Api/modules/admin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { httpInstance, createCancelTokenHandler } from './HttpApi.js' 7 | 8 | const adminJobs = { 9 | runAutoReminder() { 10 | return httpInstance.request({ 11 | method: 'GET', 12 | url: 'administration/autoreminder/run', 13 | cancelToken: 14 | cancelTokenHandlerObject[ 15 | this.runAutoReminder.name 16 | ].handleRequestCancellation().token, 17 | }) 18 | }, 19 | runJanitor() { 20 | return httpInstance.request({ 21 | method: 'GET', 22 | url: 'administration/janitor/run', 23 | cancelToken: 24 | cancelTokenHandlerObject[ 25 | this.runJanitor.name 26 | ].handleRequestCancellation().token, 27 | }) 28 | }, 29 | runNotification() { 30 | return httpInstance.request({ 31 | method: 'GET', 32 | url: 'administration/notification/run', 33 | cancelToken: 34 | cancelTokenHandlerObject[ 35 | this.runNotification.name 36 | ].handleRequestCancellation().token, 37 | }) 38 | }, 39 | } 40 | 41 | const cancelTokenHandlerObject = createCancelTokenHandler(adminJobs) 42 | 43 | export default adminJobs 44 | -------------------------------------------------------------------------------- /src/Api/modules/calendar.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { AxiosResponse } from '@nextcloud/axios' 6 | import { httpInstance, createCancelTokenHandler } from './HttpApi.js' 7 | import { Calendar } from '../../stores/preferences.js' 8 | import { CalendarEvent } from '../../components/Calendar/CalendarPeek.vue' 9 | 10 | const calendar = { 11 | getCalendars(): Promise> { 12 | return httpInstance.request({ 13 | method: 'GET', 14 | url: 'calendars', 15 | params: { time: +new Date() }, 16 | cancelToken: 17 | cancelTokenHandlerObject[ 18 | this.getCalendars.name 19 | ].handleRequestCancellation().token, 20 | }) 21 | }, 22 | getEvents( 23 | optionId: number, 24 | ): Promise> { 25 | return httpInstance.request({ 26 | method: 'GET', 27 | url: `option/${optionId}/events`, 28 | params: { 29 | tz: Intl.DateTimeFormat().resolvedOptions().timeZone, 30 | time: +new Date(), 31 | }, 32 | cancelToken: 33 | cancelTokenHandlerObject[ 34 | this.getEvents.name 35 | ].handleRequestCancellation().token, 36 | }) 37 | }, 38 | } 39 | 40 | const cancelTokenHandlerObject = createCancelTokenHandler(calendar) 41 | 42 | export default calendar 43 | -------------------------------------------------------------------------------- /src/Api/modules/session.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { AxiosResponse } from '@nextcloud/axios' 6 | import { httpInstance, createCancelTokenHandler } from './HttpApi.js' 7 | import { Session } from '../../stores/session.js' 8 | 9 | const session = { 10 | getSession(): Promise> { 11 | return httpInstance.request({ 12 | method: 'GET', 13 | url: '/session', 14 | params: { time: +new Date() }, 15 | cancelToken: 16 | cancelTokenHandlerObject[ 17 | this.getSession.name 18 | ].handleRequestCancellation().token, 19 | }) 20 | }, 21 | } 22 | 23 | const cancelTokenHandlerObject = createCancelTokenHandler(session) 24 | 25 | export default session 26 | -------------------------------------------------------------------------------- /src/Api/modules/userSettings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { AxiosResponse } from '@nextcloud/axios' 6 | import { UserPreferences } from '../../stores/preferences.js' 7 | import { httpInstance, createCancelTokenHandler } from './HttpApi.js' 8 | 9 | const userSettings = { 10 | getUserSettings(): Promise> { 11 | return httpInstance.request({ 12 | method: 'GET', 13 | url: 'preferences', 14 | params: { time: +new Date() }, 15 | cancelToken: 16 | cancelTokenHandlerObject[ 17 | this.getUserSettings.name 18 | ].handleRequestCancellation().token, 19 | }) 20 | }, 21 | 22 | writeUserSettings( 23 | preferences: UserPreferences, 24 | ): Promise> { 25 | return httpInstance.request({ 26 | method: 'POST', 27 | url: 'preferences', 28 | data: { preferences }, 29 | cancelToken: 30 | cancelTokenHandlerObject[ 31 | this.writeUserSettings.name 32 | ].handleRequestCancellation().token, 33 | }) 34 | }, 35 | } 36 | 37 | const cancelTokenHandlerObject = createCancelTokenHandler(userSettings) 38 | 39 | export default userSettings 40 | -------------------------------------------------------------------------------- /src/Api/modules/validators.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { AxiosResponse } from '@nextcloud/axios' 6 | import { httpInstance, createCancelTokenHandler } from './HttpApi.js' 7 | 8 | const validators = { 9 | validateEmailAddress( 10 | emailAddress: string, 11 | ): Promise> { 12 | return httpInstance.request({ 13 | method: 'GET', 14 | url: `check/emailaddress/${emailAddress}`, 15 | cancelToken: 16 | cancelTokenHandlerObject[ 17 | this.validateEmailAddress.name 18 | ].handleRequestCancellation().token, 19 | }) 20 | }, 21 | 22 | validateName( 23 | pollToken: string | string[], 24 | name: string, 25 | ): Promise> { 26 | return httpInstance.request({ 27 | method: 'POST', 28 | url: 'check/username', 29 | cancelToken: 30 | cancelTokenHandlerObject[ 31 | this.validateName.name 32 | ].handleRequestCancellation().token, 33 | data: { 34 | displayName: name, 35 | token: pollToken, 36 | }, 37 | headers: { 38 | 'Nc-Polls-Share-Token': pollToken, 39 | }, 40 | }) 41 | }, 42 | } 43 | 44 | const cancelTokenHandlerObject = createCancelTokenHandler(validators) 45 | 46 | export default validators 47 | -------------------------------------------------------------------------------- /src/Exceptions/Exceptions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2021 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | class Exception extends Error { 6 | constructor(message: string | undefined) { 7 | super(message) 8 | this.name = 'Exception' 9 | } 10 | } 11 | 12 | class NotReady extends Error { 13 | constructor(message: string | undefined) { 14 | super(message) 15 | this.name = 'NotReady' 16 | } 17 | } 18 | 19 | class InvalidJSON extends Error { 20 | constructor(message: string | undefined) { 21 | super(message) 22 | this.name = 'InvalidJSON' 23 | } 24 | } 25 | 26 | export { Exception, InvalidJSON, NotReady } 27 | -------------------------------------------------------------------------------- /src/adminSettings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2021 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { createApp } from 'vue' 7 | import { pinia } from './stores/index.ts' 8 | 9 | import AdminSettingsPage from './views/AdminSettingsPage.vue' 10 | 11 | // Vue.config.devtools = import.meta.env.MODE !== 'production' 12 | 13 | const Polls = createApp(AdminSettingsPage).use(pinia) 14 | Polls.mount('#content_polls') 15 | -------------------------------------------------------------------------------- /src/assets/icons/no-vote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/yes-vote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/scss/colors.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 René Gieling 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | :root { 6 | --color-polls-foreground-yes: var(--color-success); 7 | --color-polls-foreground-no: var(--color-error); 8 | --color-polls-foreground-maybe: var(--color-warning); 9 | --color-polls-background-yes: rgba(var(--color-success-rgb), 0.2); 10 | --color-polls-background-no: rgba(var(--color-error-rgb), 0.2); 11 | --color-polls-background-maybe: rgba(var(--color-warning-rgb), 0.1); 12 | --container-background-light: rgba(var(--color-info-rgb), 0.1); 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/scss/hacks.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 René Gieling 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | // some hacks, do alter the display of third parity components 6 | 7 | // FIXME: remove this hack after the issue is fixed 8 | .avatardiv .material-design-icon { 9 | width: var(--size); 10 | height: var(--size); 11 | } 12 | 13 | // h2 in notecard gets a margin-top of -6px instead of 24px for better alignment 14 | .notecard h2 { 15 | margin-top: -6px; 16 | } 17 | 18 | // relative position to be able to get sticky toolbar 19 | .app-content { 20 | position: relative !important; 21 | } 22 | 23 | .app-sidebar { 24 | top: 0 !important; 25 | } 26 | 27 | .action-item.action-item--single.app-navigation-toggle.undefined.has-tooltip { 28 | top: 4px; 29 | margin-inline-end: -50px; 30 | } 31 | 32 | // force scrolling of sidebar-tabs content 33 | .app-sidebar-tabs { 34 | overflow-y: hidden; 35 | } 36 | 37 | .user-item > .checkbox-radio-switch-switch, 38 | .user-item.add-public > .icon-add { 39 | margin-inline-end: 20px !important; 40 | } 41 | 42 | // fix v-popover sizing 43 | .poll-header-buttons { 44 | .trigger { 45 | display: inline !important; 46 | } 47 | } 48 | 49 | .modal-wrapper .modal-container { 50 | overflow: scroll !important; 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/scss/markdown.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2022 René Gieling 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | // Resets some NC classes and markdown specific styles 6 | .polls-markdown { 7 | * { 8 | margin: revert; 9 | padding: revert; 10 | font-size: revert; 11 | text-decoration: revert; 12 | list-style: revert; 13 | opacity: revert; 14 | min-height: revert; 15 | } 16 | 17 | table { 18 | border-spacing: 2px; 19 | } 20 | 21 | thead { 22 | background-color: var(--color-background-darker); 23 | color: var(--color-main-text); 24 | } 25 | 26 | td, 27 | th { 28 | padding: 1px 4px; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/assets/scss/polls-icon.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2022 René Gieling 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | .icon-polls { 6 | background-image: url(../../../img/polls.svg); 7 | filter: var(--background-invert-if-dark); 8 | } 9 | 10 | .icon-polls-dark { 11 | background-image: url(../../../img/polls-dark.svg); 12 | filter: var(--background-invert-if-dark); 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/scss/transitions.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 René Gieling 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | .transitions-active { 6 | .list-enter-active, 7 | .list-leave-active { 8 | transition: all 0.5s ease; 9 | } 10 | 11 | .list-enter-from, 12 | .list-leave-to { 13 | opacity: 0; 14 | } 15 | 16 | .list-move { 17 | transition: transform 0.5s ease; 18 | } 19 | } 20 | 21 | .v-enter-active, 22 | .v-leave-active { 23 | transition: opacity 0.5s ease; 24 | } 25 | 26 | .v-enter-from, 27 | .v-leave-to { 28 | opacity: 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Actions/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | export { default as ActionDelete } from './modules/ActionDelete.vue' 6 | export { default as ActionDeleteOrphanedVotes } from './modules/ActionDeleteOrphanedVotes.vue' 7 | export { default as ActionOpenOptionsSidebar } from './modules/ActionOpenOptionsSidebar.vue' 8 | export { default as ActionOpenSharesSidebar } from './modules/ActionOpenSharesSidebar.vue' 9 | export { default as ActionRegister } from './modules/ActionRegister.vue' 10 | export { default as ActionSendConfirmed } from './modules/ActionSendConfirmed.vue' 11 | export { default as ActionSwitchSafeTable } from './modules/ActionSwitchSafeTable.vue' 12 | export { default as ActionToggleSidebar } from './modules/ActionToggleSidebar.vue' 13 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionAddOption.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 21 | 22 | 37 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionDeleteOrphanedVotes.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | 17 | 27 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionOpenOptionsSidebar.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionOpenSharesSidebar.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionRegister.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | 38 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionSwitchSafeTable.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /src/components/Actions/modules/ActionToggleSidebar.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 23 | 24 | 37 | -------------------------------------------------------------------------------- /src/components/Activity/Activities.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /src/components/AppIcons/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | export { default as MaybeIcon } from './modules/MaybeIcon.vue' 6 | export { default as PollsAppIcon } from './modules/PollsAppIcon.vue' 7 | export { default as Spinner } from './modules/Spinner.vue' 8 | -------------------------------------------------------------------------------- /src/components/AppIcons/modules/PollsAppIcon.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | 17 | 38 | -------------------------------------------------------------------------------- /src/components/AppIcons/modules/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2025 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | export interface IconProps { 6 | title?: string 7 | fillColor?: string 8 | size?: number 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Base/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | export { default as BadgeDiv } from './modules/BadgeDiv.vue' 6 | export { default as CardDiv } from './modules/CardDiv.vue' 7 | export { default as ConfigBox } from './modules/ConfigBox.vue' 8 | export { default as DateBox } from './modules/DateBox.vue' 9 | export { default as DateTimePicker } from './modules/DateTimePicker.vue' 10 | export { default as FlexSettings } from './modules/FlexSettings.vue' 11 | export { default as FlexSpacer } from './modules/FlexSpacer.vue' 12 | export { default as HeaderBar } from './modules/HeaderBar.vue' 13 | export { default as InputDiv } from './modules/InputDiv.vue' 14 | export { default as IntersectionObserver } from './modules/IntersectionObserver.vue' 15 | export { default as LoadingOverlay } from './modules/LoadingOverlay.vue' 16 | export { default as QrModal } from './modules/QrModal.vue' 17 | export { default as RadioGroupDiv } from './modules/RadioGroupDiv.vue' 18 | export { default as Collapsible } from './modules/Collapsible.vue' 19 | -------------------------------------------------------------------------------- /src/components/Base/modules/BadgeDiv.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 18 | 19 | 54 | -------------------------------------------------------------------------------- /src/components/Base/modules/CardDiv.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 22 | 23 | 45 | -------------------------------------------------------------------------------- /src/components/Base/modules/FlexSettings.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 46 | -------------------------------------------------------------------------------- /src/components/Base/modules/FlexSpacer.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /src/components/Base/modules/IntersectionObserver.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 36 | 37 | 42 | -------------------------------------------------------------------------------- /src/components/Base/modules/LoadingOverlay.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 15 | 16 | 34 | -------------------------------------------------------------------------------- /src/components/Base/modules/RadioGroupDiv.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 34 | 35 | 49 | -------------------------------------------------------------------------------- /src/components/Cards/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | export { default as CardAddProposals } from './modules/CardAddProposals.vue' 6 | export { default as CardAnonymousPollHint } from './modules/CardAnonymousPollHint.vue' 7 | export { default as CardClosedPoll } from './modules/CardClosedPoll.vue' 8 | export { default as CardHiddenParticipants } from './modules/CardHiddenParticipants.vue' 9 | export { default as CardLimitedVotes } from './modules/CardLimitedVotes.vue' 10 | export { default as CardLocked } from './modules/CardLocked.vue' 11 | export { default as CardRegister } from './modules/CardRegister.vue' 12 | export { default as CardSendConfirmations } from './modules/CardSendConfirmations.vue' 13 | export { default as CardUnpublishedPoll } from './modules/CardUnpublishedPoll.vue' 14 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardAddProposals.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 22 | 23 | 43 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardAnonymousPollHint.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 45 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardClosedPoll.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 23 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardHiddenParticipants.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | 36 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardLocked.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardRegister.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 35 | 36 | 44 | -------------------------------------------------------------------------------- /src/components/Cards/modules/CardUnpublishedPoll.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | 28 | -------------------------------------------------------------------------------- /src/components/Combo/VoteColumn.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 23 | 24 | 34 | 35 | 46 | -------------------------------------------------------------------------------- /src/components/Comments/Comments.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | 33 | -------------------------------------------------------------------------------- /src/components/Configuration/ConfigAllowComment.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | 17 | 25 | -------------------------------------------------------------------------------- /src/components/Configuration/ConfigAllowMayBe.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /src/components/Configuration/ConfigDescription.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 |