Skip to content

Commit

Permalink
Merge pull request #264 from micromata/Release-8.1-SNAPSHOT
Browse files Browse the repository at this point in the history
Release 8.1 snapshot
Bugfixes: Skillmatrix: handling of owners, UserPrefEditForm: adding new entries.
  • Loading branch information
kreinhard authored Jan 23, 2025
2 parents 303339f + 15a4494 commit 29c4839
Show file tree
Hide file tree
Showing 20 changed files with 258 additions and 170 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2025 Micromata GmbH, Germany (www.micromata.com)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.rest.dto

import org.projectforge.plugins.skillmatrix.SkillEntryDO

class SkillEntry(
id: Long? = null,
var skill: String? = null,
var owner: User? = null,
var rating: Int? = null,
var interest: Int? = null,
var comment: String? = null,
) : BaseDTODisplayObject<SkillEntryDO>(id = id)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

package org.projectforge.plugins.skillmatrix

import jakarta.servlet.http.HttpServletRequest
import org.projectforge.business.PfCaches
import org.projectforge.business.fibu.EmployeeDO
import org.projectforge.framework.i18n.translate
import org.projectforge.framework.persistence.api.MagicFilter
import org.projectforge.framework.persistence.api.QueryFilter
Expand All @@ -31,123 +34,155 @@ import org.projectforge.framework.persistence.user.api.ThreadLocalUserContext
import org.projectforge.menu.MenuItem
import org.projectforge.menu.MenuItemTargetType
import org.projectforge.rest.config.Rest
import org.projectforge.rest.core.AbstractDOPagesRest
import org.projectforge.rest.core.AbstractDTOPagesRest
import org.projectforge.rest.dto.Employee
import org.projectforge.rest.dto.Kost1
import org.projectforge.rest.dto.SkillEntry
import org.projectforge.rest.dto.User
import org.projectforge.ui.*
import org.projectforge.ui.filter.UIFilterElement
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import jakarta.servlet.http.HttpServletRequest

@RestController
@RequestMapping("${Rest.URL}/skillentry")
class SkillEntryPagesRest() : AbstractDOPagesRest<SkillEntryDO, SkillEntryDao>(
SkillEntryDao::class.java,
"plugins.skillmatrix.title",
cloneSupport = CloneSupport.CLONE
class SkillEntryPagesRest() : AbstractDTOPagesRest<SkillEntryDO, SkillEntry, SkillEntryDao>(
SkillEntryDao::class.java,
"plugins.skillmatrix.title",
cloneSupport = CloneSupport.CLONE
) {
/**
* Initializes new memos for adding.
*/
override fun newBaseDO(request: HttpServletRequest?): SkillEntryDO {
val memo = super.newBaseDO(request)
memo.owner = ThreadLocalUserContext.loggedInUser
return memo
}
@Autowired
private lateinit var caches: PfCaches

/**
* Initializes new memos for adding.
*/
override fun newBaseDO(request: HttpServletRequest?): SkillEntryDO {
val memo = super.newBaseDO(request)
memo.owner = ThreadLocalUserContext.loggedInUser
return memo
}

override fun transformFromDB(obj: SkillEntryDO, editMode: Boolean): SkillEntry {
val entry = SkillEntry()
entry.copyFrom(obj)
caches.getUser(obj.owner?.id)?.let { userDO ->
entry.owner = User(userDO)
}
return entry
}

override fun transformForDB(dto: SkillEntry): SkillEntryDO {
val entryDO = SkillEntryDO()
dto.copyTo(entryDO)
return entryDO
}

/**
* LAYOUT List page
*/
override fun createListLayout(request: HttpServletRequest, layout: UILayout, magicFilter: MagicFilter, userAccess: UILayout.UserAccess) {
agGridSupport.prepareUIGrid4ListPage(
request,
layout,
magicFilter,
this,
userAccess = userAccess,
)
.add(lc, "lastUpdate", "skill", "owner")
.add(lc, "rating", formatter = UIAgGridColumnDef.Formatter.RATING)
.add(lc, "interest", formatter = UIAgGridColumnDef.Formatter.RATING)
.add(lc, "comment")
.withMultiRowSelection(request, magicFilter)
/**
* LAYOUT List page
*/
override fun createListLayout(
request: HttpServletRequest,
layout: UILayout,
magicFilter: MagicFilter,
userAccess: UILayout.UserAccess
) {
agGridSupport.prepareUIGrid4ListPage(
request,
layout,
magicFilter,
this,
userAccess = userAccess,
)
.add(lc, "lastUpdate", "skill", "owner")
.add(lc, "rating", formatter = UIAgGridColumnDef.Formatter.RATING)
.add(lc, "interest", formatter = UIAgGridColumnDef.Formatter.RATING)
.add(lc, "comment")
.withMultiRowSelection(request, magicFilter)

layout.add(
MenuItem(
"skillmatrix.export",
i18nKey = "exportAsXls",
url = "${SkillMatrixServicesRest.REST_EXCEL_EXPORT_PATH}",
type = MenuItemTargetType.DOWNLOAD
)
)
}
layout.add(
MenuItem(
"skillmatrix.export",
i18nKey = "exportAsXls",
url = SkillMatrixServicesRest.REST_EXCEL_EXPORT_PATH,
type = MenuItemTargetType.DOWNLOAD
)
)
}

override fun addMagicFilterElements(elements: MutableList<UILabelledElement>) {
elements.add(
UIFilterElement(
"owner",
UIFilterElement.FilterType.BOOLEAN,
translate("plugins.skillmatrix.filter.mySkills"),
defaultFilter = true
)
)
}
override fun addMagicFilterElements(elements: MutableList<UILabelledElement>) {
elements.add(
UIFilterElement(
"owner",
UIFilterElement.FilterType.BOOLEAN,
translate("plugins.skillmatrix.filter.mySkills"),
defaultFilter = true
)
)
}

override fun preProcessMagicFilter(
target: QueryFilter,
source: MagicFilter
): List<CustomResultFilter<SkillEntryDO>>? {
val ownerFilterEntry = source.entries.find { it.field == "owner" }
if (ownerFilterEntry?.isTrueValue == true) {
ownerFilterEntry.synthetic = true
target.createJoin("owner")
target.add(QueryFilter.eq("owner", ThreadLocalUserContext.loggedInUser!!)) // Show only own skills, not from others.
override fun preProcessMagicFilter(
target: QueryFilter,
source: MagicFilter
): List<CustomResultFilter<SkillEntryDO>>? {
val ownerFilterEntry = source.entries.find { it.field == "owner" }
if (ownerFilterEntry?.isTrueValue == true) {
ownerFilterEntry.synthetic = true
target.createJoin("owner")
target.add(
QueryFilter.eq(
"owner",
ThreadLocalUserContext.loggedInUser!!
)
) // Show only own skills, not from others.
}
return null
}
return null
}

/**
* Sets logged-in-user as owner.
*/
override fun prepareClone(dto: SkillEntryDO): SkillEntryDO {
val clone = super.prepareClone(dto)
clone.owner = ThreadLocalUserContext.loggedInUser
return clone
}
/**
* Sets logged-in-user as owner.
*/
override fun prepareClone(dto: SkillEntry): SkillEntry {
val clone = super.prepareClone(dto)
clone.owner = User(ThreadLocalUserContext.requiredLoggedInUser)
return clone
}

/**
* LAYOUT Edit page
*/
override fun createEditLayout(dto: SkillEntryDO, userAccess: UILayout.UserAccess): UILayout {
val skillRating = UIRatingStars(
"rating",
lc,
Array(SkillEntryDO.MAX_VAL_RATING + 1) { idx -> translate("plugins.skillmatrix.rating.$idx") },
label = "plugins.skillmatrix.rating"
)
val interestRating = UIRatingStars(
"interest",
lc,
Array(SkillEntryDO.MAX_VAL_INTEREST + 1) { idx -> translate("plugins.skillmatrix.interest.$idx") },
label = "plugins.skillmatrix.interest"
)
val skill = UIInput("skill", lc).enableAutoCompletion(this)
val layout = super.createEditLayout(dto, userAccess)
.add(skill)
.add(
UIRow()
.add(
UICol(UILength(md = 4)).add(
UIReadOnlyField(
"owner.displayName",
lc,
label = "plugins.skillmatrix.owner"
)
/**
* LAYOUT Edit page
*/
override fun createEditLayout(dto: SkillEntry, userAccess: UILayout.UserAccess): UILayout {
val skillRating = UIRatingStars(
"rating",
lc,
Array(SkillEntryDO.MAX_VAL_RATING + 1) { idx -> translate("plugins.skillmatrix.rating.$idx") },
label = "plugins.skillmatrix.rating"
)
val interestRating = UIRatingStars(
"interest",
lc,
Array(SkillEntryDO.MAX_VAL_INTEREST + 1) { idx -> translate("plugins.skillmatrix.interest.$idx") },
label = "plugins.skillmatrix.interest"
)
val skill = UIInput("skill", lc).enableAutoCompletion(this)
val layout = super.createEditLayout(dto, userAccess)
.add(skill)
.add(
UIRow()
.add(
UICol(UILength(md = 4)).add(
UIReadOnlyField(
"owner.displayName",
lc,
label = "plugins.skillmatrix.owner"
)
)
)
.add(UICol(UILength(md = 4)).add(skillRating))
.add(UICol(UILength(md = 4)).add(interestRating))
)
)
.add(UICol(UILength(md = 4)).add(skillRating))
.add(UICol(UILength(md = 4)).add(interestRating))
)
.add(lc, "comment")
return LayoutUtils.processEditPage(layout, dto, this)
}
.add(lc, "comment")
return LayoutUtils.processEditPage(layout, dto, this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import org.projectforge.framework.access.GroupTaskAccessDO
import org.projectforge.framework.access.OperationType
import org.projectforge.framework.cache.AbstractCache
import org.projectforge.framework.i18n.InternalErrorException
import org.projectforge.framework.persistence.api.HibernateUtils
import org.projectforge.framework.persistence.jpa.PfPersistenceService
import org.projectforge.framework.time.DateHelper
import org.projectforge.framework.utils.NumberHelper.greaterZero
Expand Down Expand Up @@ -273,7 +274,7 @@ class TaskTree : AbstractCache(TICKS_PER_HOUR),
*/
fun getTaskIfNotInitialized(task: TaskDO?): TaskDO? {
task ?: return null
if (Hibernate.isInitialized(task)) {
if (HibernateUtils.isFullyInitialized(task)) {
return task
}
return getTaskById(task.id)
Expand Down Expand Up @@ -570,7 +571,7 @@ class TaskTree : AbstractCache(TICKS_PER_HOUR),
get() {
synchronized(this) {
if (this.orderPositionReferencesDirty) {
log.info{"TaskTree: refreshing order position references..."}
log.info { "TaskTree: refreshing order position references..." }
val duration = LogDuration()
val references = mutableMapOf<Long, MutableSet<OrderPositionInfo>>()
persistenceService.runIsolatedReadOnly { context ->
Expand Down Expand Up @@ -600,7 +601,7 @@ class TaskTree : AbstractCache(TICKS_PER_HOUR),
}
this.orderPositionReferences = references
this.orderPositionReferencesDirty = false
log.info{"TaskTree: refreshing order position references done: $duration"}
log.info { "TaskTree: refreshing order position references done: $duration" }
}
return this.orderPositionReferences
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ package org.projectforge.business.address
import jakarta.annotation.PostConstruct
import jakarta.persistence.Tuple
import mu.KotlinLogging
import org.hibernate.Hibernate
import org.projectforge.business.fibu.kost.Kost1DO
import org.projectforge.framework.access.OperationType
import org.projectforge.framework.cache.AbstractCache
import org.projectforge.framework.persistence.api.BaseDOModifiedListener
import org.projectforge.framework.persistence.api.HibernateUtils
import org.projectforge.framework.persistence.database.TupleUtils.getLong
import org.projectforge.framework.persistence.jpa.PfPersistenceService
import org.springframework.beans.factory.annotation.Autowired
Expand Down Expand Up @@ -76,7 +75,7 @@ open class AddressbookCache : AbstractCache() {
*/
fun getAddressbookIfNotInitialized(ab: AddressbookDO?): AddressbookDO? {
ab ?: return null
if (Hibernate.isInitialized(ab)) {
if (HibernateUtils.isFullyInitialized(ab)) {
return ab
}
return getAddressbook(ab.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ package org.projectforge.business.fibu

import jakarta.annotation.PostConstruct
import mu.KotlinLogging
import org.hibernate.Hibernate
import org.projectforge.business.user.UserGroupCache
import org.projectforge.framework.cache.AbstractCache
import org.projectforge.framework.persistence.api.HibernateUtils
import org.projectforge.framework.persistence.jpa.PfPersistenceService
import org.projectforge.framework.persistence.user.entities.PFUserDO
import org.springframework.beans.factory.annotation.Autowired
Expand Down Expand Up @@ -70,7 +70,7 @@ open class EmployeeCache : AbstractCache() {
*/
fun getEmployeeIfNotInitialized(employee: EmployeeDO?): EmployeeDO? {
employee ?: return null
if (Hibernate.isInitialized(employee) && employee.user != null) {
if (HibernateUtils.isFullyInitialized(employee) && employee.user != null) {
return employee
}
return getEmployee(employee.id)
Expand Down
Loading

0 comments on commit 29c4839

Please sign in to comment.