Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added

- Support Nextcloud 35
- Log integration settings saved through the setup API and every new Nextcloud OAuth client for OpenProject to the audit log
- Log the project folder setup and terms of service signed for the OpenProject user to the audit log
- Log users connecting and disconnecting their OpenProject account to the audit log
- Log files linked to and unlinked from OpenProject work packages to the audit log

### Fixed

- Keep the admin settings when Nextcloud replays the migrations to check the database schema (`occ db:schema:check`, every `occ upgrade` on Nextcloud 35)
- Selecting an OpenProject work package does not insert the reference in the Smart Picker [#1200](https://github.com/nextcloud/integration_openproject/pull/1200)

### Changed

- Make OAuth2 Client class usage compatible with older Nextcloud versions [#1148](https://github.com/nextcloud/integration_openproject/pull/1148)
- Add owner_file_name property in filesInfo endpoint response [#1189](https://github.com/nextcloud/integration_openproject/pull/1189)
- Write the audit log entries through the admin_audit app, with an operation and named parameters, whenever the app is enabled
Expand Down
42 changes: 26 additions & 16 deletions src/components/tab/SearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
-->
<template>
<div id="searchBar">
<NcSelect ref="workPackageSelect"
<NcSelect
ref="workPackageSelect"
class="searchInput"
input-id="searchInput"
:placeholder="placeholder"
Expand All @@ -17,7 +18,7 @@
:clear-search-on-blur="() => false"
:disabled="isDisabled"
@search="asyncFind"
@option:selected="linkWorkPackageToFile">
@option:selected="selectWorkPackage">
<template #option="option">
<WorkPackage :key="option.id"
:workpackage="option"
Expand Down Expand Up @@ -190,7 +191,7 @@ export default {
if (data.openProjectEventName === 'work_package_creation_success') {
showSuccess(t('integration_openproject', 'Work package created successfully.'))
this.newWorkpackageCreated = true
await this.linkWorkPackageToFile(data.openProjectEventPayload)
await this.linkWorkPackage(data.openProjectEventPayload)
}
},
onCloseCreateWorkPackageModalEvent() {
Expand Down Expand Up @@ -222,19 +223,18 @@ export default {
await this.debounceMakeSearchRequest(query, null)
}
},
async getWorkPackageLink(selectedOption) {
return this.openprojectUrl.replace(/^\/+|\/+$/g, '') + '/wp/' + selectedOption.id
getWorkPackageLink(workPackageID) {
return this.openprojectUrl.replace(/^\/+|\/+$/g, '') + '/wp/' + workPackageID
},
debounceMakeSearchRequest: debounce(function(...args) {
if (args[0].length < SEARCH_CHAR_LIMIT) return
return this.makeSearchRequest(...args)
}, DEBOUNCE_THRESHOLD),
async linkWorkPackageToFile(selectedOption) {
if (this.isSmartPicker) {
const link = await this.getWorkPackageLink(selectedOption)
this.$emit('submit', link)
return
}
submitSelectedWorkPackageSmartPicker(workPackageID) {
const link = this.getWorkPackageLink(workPackageID)
this.$emit('submit', link)
},
async linkWorkPackage(selectedOption) {
// since we can link multiple files now we send file information required in an array (whether it's only one value or multiple)
if (this.searchOrigin === WORKPACKAGES_SEARCH_ORIGIN.PROJECT_TAB) {
if (this.newWorkpackageCreated && this.searchOrigin === WORKPACKAGES_SEARCH_ORIGIN.PROJECT_TAB) {
Expand All @@ -245,17 +245,27 @@ export default {
}
await workpackageHelper.linkFileToWorkPackageWithSingleRequest([this.fileInfo], selectedOption, t('integration_openproject', 'Link to work package created successfully!'), this)
this.resetState()
} else if (this.searchOrigin === WORKPACKAGES_SEARCH_ORIGIN.LINK_MULTIPLE_FILES_MODAL) {
return
}

if (this.searchOrigin === WORKPACKAGES_SEARCH_ORIGIN.LINK_MULTIPLE_FILES_MODAL) {
if (this.fileInfo.length <= 20) {
await workpackageHelper.linkFileToWorkPackageWithSingleRequest(this.fileInfo, selectedOption, t('integration_openproject', 'Links to work package created successfully for selected files!'), this)
this.$emit('close', selectedOption)
this.resetState()
} else {
// the selected files will be linked in chunks
await workpackageHelper.linkMultipleFilesToWorkPackageWithChunking(this.fileInfo, selectedOption, false, this)
this.resetState()
return
}
// the selected files will be linked in chunks
await workpackageHelper.linkMultipleFilesToWorkPackageWithChunking(this.fileInfo, selectedOption, false, this)
this.resetState()
}
},
async selectWorkPackage(selectedOption) {
if (this.isSmartPicker) {
this.submitSelectedWorkPackageSmartPicker(selectedOption?.id)
return
}
await this.linkWorkPackage(selectedOption)
},
async makeSearchRequest(search, fileId) {
this.state = STATE.LOADING
Expand Down
20 changes: 13 additions & 7 deletions src/reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ __webpack_nonce__ = btoa(OC.requestToken)
/* eslint-disable-next-line no-undef */
__webpack_public_path__ = OC.linkTo(APP_ID, 'js/')

// this is where we associate our widget component with the richobjects that we return in the reference provider
const workPackageCards = new WeakMap()
// INFO: this is where we associate our widget component with the rich-objects
// that we return in the reference provider.
// Link preview card in the chat.
registerWidget(`${APP_ID}_work_package`, async (el, { richObjectType, richObject, accessible }) => {
// here we lazy load the components so it does not slow down the initial page load
const { createApp } = await import(/* webpackChunkName: "reference-wp-lazy" */'vue')
Expand All @@ -28,20 +31,23 @@ registerWidget(`${APP_ID}_work_package`, async (el, { richObjectType, richObject
})
setupGlobalProperties(widget)
widget.mount(el)
workPackageCards.set(el, widget)
}, (el) => {
workPackageCards.get(el)?.unmount()
workPackageCards.delete(el)
})

// INFO: register OpenProject work packages search component inside the Smart Picker.
// Shows OpenProject smart picker when using "/" command in the chat
registerCustomPickerElement('openproject-work-package-ref', async (el, { providerId, accessible }) => {
const { createApp } = await import(/* webpackChunkName: "reference-picker-lazy" */'vue')
const { default: WorkPackagePickerElement } = await import(/* webpackChunkName: "reference-picker-lazy" */'./views/WorkPackagePickerElement.vue')

const app = createApp(WorkPackagePickerElement, {
providerId,
accessible,
})
const app = createApp(WorkPackagePickerElement)
setupGlobalProperties(app)
const vueElement = app.mount(el)

return new NcCustomPickerRenderResult(vueElement.$el, vueElement)
return new NcCustomPickerRenderResult(vueElement.$el, app)
}, (el, renderResult) => {
renderResult.object.$destroy()
renderResult.object.unmount()
})
14 changes: 1 addition & 13 deletions src/views/WorkPackagePickerElement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,6 @@ export default {
SearchInput,
NcLoadingIcon,
},

props: {
providerId: {
type: String,
required: true,
},
accessible: {
type: Boolean,
default: false,
},
},
emits: ['submit'],
data() {
return {
fileInfo: {},
Expand Down Expand Up @@ -86,7 +74,7 @@ export default {
},
methods: {
onSubmit(data) {
this.$emit('submit', data)
this.$el.dispatchEvent(new CustomEvent('submit', { detail: data }))
},
async checkIfOpenProjectIsAvailable() {
if (!this.adminConfigState.isAdminConfigOk) {
Expand Down
Loading