PageRenderTime 32ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/resources/com/onresolve/jira/groovy/canned/admin/RenameUser43.groovy

https://bitbucket.org/sorin/jira-plugin-intellij
Groovy | 917 lines | 759 code | 112 blank | 46 comment | 126 complexity | 49c3eed1bff74412243a3be699e3c418 MD5 | raw file
  1. package com.onresolve.jira.groovy.canned.admin
  2. import com.atlassian.jira.ComponentManager
  3. import com.atlassian.jira.util.ErrorCollection
  4. import com.onresolve.jira.groovy.canned.CannedScript
  5. import com.opensymphony.user.UserManager
  6. import org.apache.log4j.Category
  7. import com.atlassian.query.clause.Clause
  8. import com.atlassian.query.clause.TerminalClauseImpl
  9. import com.atlassian.query.operand.Operand
  10. import com.atlassian.query.operand.SingleValueOperand
  11. import com.atlassian.query.clause.OrClause
  12. import com.atlassian.jira.issue.search.SearchRequestManager
  13. import com.atlassian.jira.issue.search.SearchRequest
  14. import com.atlassian.jira.bc.filter.SearchRequestService
  15. import com.atlassian.jira.util.collect.EnclosedIterable
  16. import com.atlassian.jira.util.Consumer
  17. import com.atlassian.query.clause.ClauseVisitor
  18. import com.atlassian.query.clause.AndClause
  19. import com.atlassian.query.clause.NotClause
  20. import com.atlassian.query.clause.TerminalClause
  21. import com.atlassian.query.operand.OperandVisitor
  22. import com.atlassian.query.operand.EmptyOperand
  23. import com.atlassian.query.operand.FunctionOperand
  24. import com.atlassian.query.operand.MultiValueOperand
  25. import com.atlassian.query.Query
  26. import com.atlassian.query.QueryImpl
  27. import com.atlassian.jira.issue.search.managers.SearchHandlerManager
  28. import com.atlassian.jira.jql.ClauseHandler
  29. import com.atlassian.jira.issue.CustomFieldManager
  30. import com.atlassian.jira.issue.customfields.CustomFieldType
  31. import com.atlassian.jira.issue.fields.UserField
  32. import com.atlassian.jira.bc.JiraServiceContextImpl
  33. import com.atlassian.core.ofbiz.util.CoreTransactionUtil
  34. import com.atlassian.jira.event.ClearCacheEvent
  35. import com.atlassian.event.api.EventPublisher
  36. import com.atlassian.jira.imports.project.parser.UserAssociationParser
  37. import org.ofbiz.core.entity.GenericValue
  38. import com.atlassian.jira.util.ImportUtils
  39. import com.atlassian.jira.issue.index.IssueIndexManager
  40. import com.atlassian.jira.ManagerFactory
  41. import com.atlassian.jira.issue.IssueManager
  42. import org.ofbiz.core.entity.DelegatorInterface
  43. import org.ofbiz.core.entity.EntityOperator
  44. import org.ofbiz.core.entity.EntityExpr
  45. import org.ofbiz.core.entity.EntityConditionList
  46. import com.atlassian.jira.security.roles.actor.UserRoleActorFactory
  47. import com.atlassian.jira.security.roles.CachingProjectRoleAndActorStore
  48. import com.atlassian.jira.notification.type.SingleUser
  49. import com.atlassian.jira.issue.security.IssueSecuritySchemeManagerImpl
  50. import com.atlassian.jira.permission.DefaultPermissionSchemeManager
  51. import com.atlassian.jira.notification.DefaultNotificationSchemeManager
  52. import com.opensymphony.user.User
  53. import com.atlassian.jira.issue.search.CachingSearchRequestStore
  54. import com.atlassian.jira.util.SimpleErrorCollection
  55. import com.atlassian.core.user.UserUtils
  56. import com.atlassian.jira.util.BuildUtils
  57. import com.atlassian.query.clause.WasClause
  58. public class RenameUser implements CannedScript {
  59. Category log = Category.getInstance(RenameUser.class)
  60. UserManager userManager = UserManager.getInstance()
  61. ComponentManager componentManager = ComponentManager.getInstance()
  62. SearchRequestManager searchRequestManager = componentManager.getSearchRequestManager()
  63. def searchRequestService = componentManager.getSearchRequestService()
  64. public static final String FIELD_FROM_USER_ID = 'FIELD_FROM_USER_ID'
  65. public static final String FIELD_TO_USER_ID = 'FIELD_TO_USER_ID'
  66. public static final String FIELD_MERGE = 'FIELD_MERGE'
  67. List msgs = []
  68. String msg
  69. Set<Long> reindexIssueIds = new HashSet()
  70. boolean preview = false
  71. Long nSearchReqsUpdated = 0
  72. boolean doMerge
  73. public String getName() {
  74. "Renames a user ID"
  75. }
  76. public String getDescription() {
  77. "Changes a user's ID without reindexing or restarting"
  78. }
  79. List getCategories() {
  80. ["ADMIN"]
  81. }
  82. public Boolean isFinalParamsPage(Map params) {
  83. true
  84. }
  85. public List getParameters(Map params) {
  86. [
  87. [
  88. Name: FIELD_FROM_USER_ID,
  89. Label: 'From user ID',
  90. Description: "Rename or merge from who?",
  91. ],
  92. [
  93. Name: FIELD_TO_USER_ID,
  94. Label: 'To user ID',
  95. Description: "Rename or merge to who?",
  96. ],
  97. [
  98. Name: FIELD_MERGE,
  99. Label: 'Merge',
  100. Type: 'radio',
  101. Description: "Merge the first user to the second user, rather than rename",
  102. Values: ["false": "Rename", "true": "Merge"],
  103. ],
  104. ]
  105. }
  106. ErrorCollection doValidate(Map params, boolean forPreview) {
  107. ErrorCollection errorCollection = new SimpleErrorCollection()
  108. String sourceUserId = params[FIELD_FROM_USER_ID] as String
  109. String targetUserId = params[FIELD_TO_USER_ID] as String
  110. if (! params[FIELD_MERGE]) {
  111. errorCollection.addError(FIELD_MERGE, "Please select an option")
  112. }
  113. doMerge = Boolean.parseBoolean(params[FIELD_MERGE] as String)
  114. [FIELD_FROM_USER_ID, FIELD_TO_USER_ID].each {String p ->
  115. if (! params[p]) {
  116. errorCollection.addError(p, "Please provide the user ID")
  117. }
  118. }
  119. if (errorCollection.hasAnyErrors()) {
  120. return errorCollection
  121. }
  122. if (! UserUtils.existsUser(sourceUserId as String)) {
  123. errorCollection.addError(FIELD_FROM_USER_ID, "Cannot find user ID")
  124. }
  125. if (sourceUserId == targetUserId) {
  126. errorCollection.addErrorMessage("From and to user can't be the same")
  127. }
  128. boolean targetExists = UserUtils.existsUser(targetUserId as String)
  129. if (doMerge && ! targetExists) {
  130. errorCollection.addError(FIELD_TO_USER_ID, "Target user ID must exist already for a merge")
  131. }
  132. if (! doMerge && targetExists) {
  133. errorCollection.addError(FIELD_TO_USER_ID, "Target user ID must not exist already")
  134. }
  135. if (sourceUserId == componentManager.getJiraAuthenticationContext().getUser()?.name) {
  136. errorCollection.addError(FIELD_FROM_USER_ID, "You can't rename the logged in user")
  137. }
  138. errorCollection
  139. }
  140. Query transformSearchRequest(Query query, String sourceUser, String targetUser) {
  141. Clause whereClause = query.getWhereClause()
  142. Clause newWhereClause = whereClause.accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause
  143. // log.debug("original whereClause: " + query.whereClause.toString())
  144. // log.debug "new where clause: " + newWhereClause.toString()
  145. Query newQuery = new QueryImpl(newWhereClause)
  146. newQuery = new QueryImpl(newWhereClause, query.orderByClause, newQuery.queryString)
  147. newQuery
  148. }
  149. Map doScript(Map params) {
  150. String sourceUser = params[FIELD_FROM_USER_ID] as String
  151. String targetUser = params[FIELD_TO_USER_ID] as String
  152. doMerge = Boolean.parseBoolean(params[FIELD_MERGE] as String)
  153. // make sure they're not logged on??
  154. if (!preview) {
  155. msgs << "<b>Completed</b><br>"
  156. }
  157. updateGenericValues(sourceUser, targetUser)
  158. updateAllSearchRequests(sourceUser, targetUser)
  159. if (!preview) {
  160. msg = "Completed ${doMerge ? "merge" : "rename"} of $sourceUser to $targetUser"
  161. log.warn(msg)
  162. msgs << msg
  163. }
  164. params["output"] = "<pre>" + msgs.join("\n") + "<pre>"
  165. params
  166. }
  167. private def reindexIssue(IssueManager issueManager, Long issueId, Category log, IssueIndexManager indexManager) {
  168. GenericValue issue = issueManager.getIssue(issueId)
  169. boolean wasIndexing = ImportUtils.isIndexIssues();
  170. ImportUtils.setIndexIssues(true);
  171. if ((BuildUtils.getCurrentBuildNumber() as Long) < 614) {
  172. ManagerFactory.getCacheManager().flush(com.atlassian.jira.issue.cache.CacheManager.ISSUE_CACHE, issue)
  173. }
  174. log.debug("Reindex issue ${issue.key}")
  175. indexManager.reIndex(issue);
  176. ImportUtils.setIndexIssues(wasIndexing)
  177. }
  178. def updateGenericValues(String sourceUser, String targetUser) {
  179. boolean began = false
  180. DelegatorInterface gd = (DelegatorInterface) componentManager.getComponentInstanceOfType(DelegatorInterface.class)
  181. IssueIndexManager indexManager = ComponentManager.getInstance().getIndexManager()
  182. CachingProjectRoleAndActorStore cachingProjectRoleAndActorStore = ComponentManager.getComponentInstanceOfType(CachingProjectRoleAndActorStore.class) as CachingProjectRoleAndActorStore
  183. DefaultNotificationSchemeManager notificationSchemeManager = ComponentManager.getComponentInstanceOfType(DefaultNotificationSchemeManager.class) as DefaultNotificationSchemeManager
  184. DefaultPermissionSchemeManager defaultPermissionSchemeManager = ComponentManager.getComponentInstanceOfType(DefaultPermissionSchemeManager.class) as DefaultPermissionSchemeManager
  185. IssueSecuritySchemeManagerImpl securitySchemeManager = ComponentManager.getComponentInstanceOfType(IssueSecuritySchemeManagerImpl.class) as IssueSecuritySchemeManagerImpl
  186. CachingSearchRequestStore cachingSearchRequestStore = ComponentManager.getComponentInstanceOfType(CachingSearchRequestStore.class) as CachingSearchRequestStore
  187. IssueManager issueManager = componentManager.getIssueManager()
  188. if (! preview) {
  189. log.debug("Begin transaction")
  190. began = CoreTransactionUtil.begin();
  191. }
  192. try {
  193. msg = "Beginning ${doMerge ? "merge" : "rename"} of $sourceUser to $targetUser"
  194. log.warn(msg)
  195. msgs << msg
  196. // Rename the user record itself
  197. if ((BuildUtils.getCurrentBuildNumber() as Long) < 614) {
  198. List gvs = gd.findByAnd("OSUser", [name: sourceUser])
  199. assert gvs.size() == 1
  200. if (! preview) {
  201. GenericValue usergv = gvs.first() as GenericValue
  202. if (doMerge) {
  203. // todo: delete their associated property records
  204. usergv.remove()
  205. }
  206. else {
  207. usergv.set("name", targetUser)
  208. usergv.store()
  209. }
  210. }
  211. // group memberships
  212. gvs = gd.findByAnd("OSMembership", [userName: sourceUser])
  213. msg = "Update ${gvs.size()} OSMembership records"
  214. log.debug(msg)
  215. msgs << msg
  216. if (!preview) {
  217. List<GenericValue> targetGvs = gd.findByAnd("OSMembership", [userName: targetUser])
  218. List<Object> targetGroups = targetGvs*.get("groupName")
  219. gvs.each {GenericValue gv ->
  220. if (doMerge && targetGroups.contains (gv.get("groupName"))) {
  221. gv.remove()
  222. }
  223. else {
  224. gv.set("userName", targetUser)
  225. gv.store()
  226. }
  227. }
  228. }
  229. }
  230. else {
  231. // todo: only look in jira internal directory
  232. List gvs = gd.findByAnd("User", [userName: sourceUser])
  233. assert gvs.size() == 1
  234. if (! preview) {
  235. GenericValue usergv = gvs.first() as GenericValue
  236. if (doMerge) {
  237. usergv.remove()
  238. }
  239. else {
  240. usergv.set("userName", targetUser)
  241. usergv.set("lowerUserName", targetUser.toLowerCase())
  242. usergv.store()
  243. }
  244. }
  245. // group memberships
  246. gvs = gd.findByAnd("Membership", [childName: sourceUser, directoryId: 1, membershipType: "GROUP_USER"])
  247. msg = "Update ${gvs.size()} Membership records"
  248. log.debug(msg)
  249. msgs << msg
  250. if (!preview) {
  251. List<GenericValue> targetGvs = gd.findByAnd("Membership", [childName: targetUser, directoryId: 1, membershipType: "GROUP_USER"])
  252. List<Object> targetGroups = targetGvs*.get("parentName")
  253. gvs.each {GenericValue gv ->
  254. if (doMerge && targetGroups.contains (gv.get("parentName"))) {
  255. gv.remove()
  256. }
  257. else {
  258. gv.set("childName", targetUser)
  259. gv.set("lowerChildName", targetUser)
  260. gv.store()
  261. }
  262. }
  263. }
  264. }
  265. // trustedapp
  266. List gvs = gd.findByAnd("TrustedApplication", [createdBy: sourceUser])
  267. msg = "Update ${gvs.size()} TrustedApplication records"
  268. log.debug(msg)
  269. msgs << msg
  270. if (!preview) {
  271. gvs.each {GenericValue gv ->
  272. gv.set("createdBy", targetUser)
  273. gv.store()
  274. }
  275. }
  276. // SearchRequest ownership
  277. // if this is a merge, we may get dupe filter names. Not ideal but handled OK by the UI
  278. gvs = gd.findByOr("SearchRequest", [user: sourceUser, author: sourceUser])
  279. msg = "Update ${gvs.size()} SearchRequest records"
  280. log.debug(msg)
  281. msgs << msg
  282. if (!preview) {
  283. gvs.each {GenericValue gv ->
  284. if (gv.get("user")) {
  285. gv.set("user", targetUser)
  286. }
  287. if (gv.get("author")) {
  288. gv.set("author", targetUser)
  289. }
  290. cachingSearchRequestStore.removeFromCache(gv.getLong("id"))
  291. gv.store()
  292. }
  293. }
  294. // ColumnLayout - choose one or tother.
  295. gvs = gd.findByAnd("ColumnLayout", [username: sourceUser])
  296. msg = "Update ${gvs.size()} ColumnLayout records"
  297. List<GenericValue> targetGvs = gd.findByAnd("ColumnLayout", [username: targetUser])
  298. log.debug(msg)
  299. msgs << msg
  300. if (!preview) {
  301. gvs.each {GenericValue gv ->
  302. if (doMerge && targetGvs.find {it.getLong("searchrequest") == gv.getLong("searchrequest")}) {
  303. gd.removeRelated("ChildColumnLayoutItem", gv)
  304. gv.remove()
  305. }
  306. else {
  307. gv.set("username", targetUser)
  308. gv.store()
  309. }
  310. }
  311. }
  312. if (!doMerge) {
  313. gvs = gd.findByAnd("UserHistoryItem", [username: sourceUser])
  314. msg = "Update ${gvs.size()} UserHistoryItem records"
  315. log.debug(msg)
  316. msgs << msg
  317. if (!preview) {
  318. gvs.each {GenericValue gv ->
  319. gv.set("username", targetUser)
  320. gv.store()
  321. }
  322. }
  323. }
  324. gvs = gd.findByAnd("FavouriteAssociations", [username: sourceUser])
  325. targetGvs = gd.findByAnd("FavouriteAssociations", [username: targetUser])
  326. msg = "Update ${gvs.size()} FavouriteAssociations records"
  327. log.debug(msg)
  328. msgs << msg
  329. if (!preview) {
  330. List matchFields = ["entityType", "entityId"]
  331. gvs.each {GenericValue gv ->
  332. // favourite assocs, only fave if not already faved
  333. if (doMerge && targetGvs.find {
  334. it.getFields(matchFields) == gv.getFields(matchFields)
  335. }) {
  336. // favcount must be adjusted downwards when both are favouriting
  337. log.debug("Remove fav association record and decrement favourite count")
  338. GenericValue sharedEntityGv = gd.findByPrimaryKey(gv.get("entityType") as String, [id: gv.get("entityId")])
  339. sharedEntityGv.set("favCount", (sharedEntityGv.get("favCount") as Long) - 1)
  340. sharedEntityGv.store()
  341. gv.remove()
  342. }
  343. else {
  344. gv.set("username", targetUser)
  345. gv.store()
  346. }
  347. }
  348. }
  349. ["PortalPage", "FilterSubscription"].each {String entityName ->
  350. gvs = gd.findByAnd(entityName, [username: sourceUser])
  351. msg = "Update ${gvs.size()} $entityName records"
  352. log.debug(msg)
  353. msgs << msg
  354. if (!preview) {
  355. gvs.each {GenericValue gv ->
  356. gv.set("username", targetUser)
  357. gv.store()
  358. }
  359. }
  360. }
  361. // note: for versions prior to 4.1 the favouritesStoreCache needs to be flushed
  362. // Core issue fields
  363. gvs = gd.findByOr("Issue", [reporter: sourceUser, assignee: sourceUser])
  364. msg = "Update ${gvs.size()} Issue records"
  365. log.debug(msg)
  366. msgs << msg
  367. gvs.each {GenericValue gv ->
  368. if (!preview) {
  369. if (gv.get("reporter") == sourceUser) {
  370. gv.set("reporter", targetUser)
  371. }
  372. if (gv.get("assignee") == sourceUser) {
  373. gv.set("assignee", targetUser)
  374. }
  375. gv.store()
  376. }
  377. reindexIssueIds.add(gv.getLong("id"))
  378. }
  379. // issue transition history
  380. gvs = gd.findByOr("Action", [author: sourceUser, updateauthor: sourceUser])
  381. msg = "Update ${gvs.size()} Action records"
  382. log.debug(msg)
  383. msgs << msg
  384. if (!preview) {
  385. gvs.each {GenericValue gv ->
  386. if (gv.get("author") == sourceUser) {
  387. gv.set("author", targetUser)
  388. }
  389. if (gv.get("updateauthor") == sourceUser) {
  390. gv.set("updateauthor", targetUser)
  391. }
  392. gv.store()
  393. }
  394. }
  395. // change groups
  396. gvs = gd.findByAnd("ChangeGroup", [author: sourceUser])
  397. msg = "Update ${gvs.size()} ChangeGroup records"
  398. log.debug(msg)
  399. msgs << msg
  400. if (!preview) {
  401. gvs.each {GenericValue gv ->
  402. gv.set("author", targetUser)
  403. gv.store()
  404. }
  405. }
  406. // Change items
  407. // todo: custom field history items - problem is they are referenced by name which is not unique
  408. EntityConditionList conditionList = new EntityConditionList(
  409. [new EntityConditionList(
  410. [
  411. // Stoopid oracle can't do equals on a clob, LIKE shd be OK
  412. // alternative is to do the filtering in the code
  413. new EntityExpr("oldvalue", EntityOperator.LIKE, sourceUser),
  414. new EntityExpr("newvalue", EntityOperator.LIKE, sourceUser),
  415. ], EntityOperator.OR),
  416. new EntityConditionList([new EntityExpr("field", EntityOperator.IN, ["reporter", "assignee"])], EntityOperator.AND)
  417. ], EntityOperator.AND
  418. )
  419. gvs = gd.findByCondition("ChangeItem", conditionList, ["id", "field", "oldvalue", "newvalue"], Collections.<String> emptyList())
  420. msg = "Update ${gvs.size()} ChangeItem records"
  421. log.debug(msg)
  422. msgs << msg
  423. if (!preview) {
  424. gvs.each {GenericValue gv ->
  425. if (gv.get("oldvalue") == sourceUser) {
  426. gv.set("oldvalue", targetUser)
  427. }
  428. if (gv.get("newvalue") == sourceUser) {
  429. gv.set("newvalue", targetUser)
  430. }
  431. gv.store()
  432. }
  433. }
  434. // Watchers and voters
  435. gvs = gd.findByAnd("UserAssociation", getUserAssociationConditionList(sourceUser))
  436. targetGvs = gd.findByAnd("UserAssociation", getUserAssociationConditionList(targetUser))
  437. msg = "Update ${gvs.size()} UserAssociation records"
  438. log.debug(msg)
  439. msgs << msg
  440. gvs.each {GenericValue gv ->
  441. if (gv.get("sinkNodeEntity") == "Issue") {
  442. reindexIssueIds.add(gv.getLong("sinkNodeId"))
  443. }
  444. if (!preview) {
  445. gd.removeValue(gv)
  446. if (doMerge && targetGvs.find {
  447. it.get("sinkNodeId") == gv.get("sinkNodeId") &&
  448. it.get("sinkNodeEntity") == gv.get("sinkNodeEntity") &&
  449. it.get("associationType") == gv.get("associationType")
  450. }) {
  451. // do nothing as always remove
  452. log.debug("Removed userassoc record")
  453. }
  454. else {
  455. // gv.store() doesn't work because we're changing the primary key
  456. gv.set("sourceName", targetUser)
  457. gd.create(gv)
  458. }
  459. }
  460. }
  461. // roles
  462. gvs = gd.findByAnd("ProjectRoleActor", [roletype: UserRoleActorFactory.TYPE, roletypeparameter: sourceUser])
  463. targetGvs = gd.findByAnd("ProjectRoleActor", [roletype: UserRoleActorFactory.TYPE, roletypeparameter: targetUser])
  464. msg = "Update ${gvs.size()} ProjectRoleActor records"
  465. log.debug(msg)
  466. msgs << msg
  467. if (!preview) {
  468. gvs.each {GenericValue gv ->
  469. List matchFields = ["pid", "projectroleid"]
  470. if (doMerge && targetGvs.find {
  471. gv.getFields(matchFields) == it.getFields(matchFields)
  472. }) {
  473. log.debug("Remove role record")
  474. gv.remove()
  475. }
  476. else {
  477. gv.set("roletypeparameter", targetUser)
  478. gv.store()
  479. }
  480. }
  481. cachingProjectRoleAndActorStore.clearCaches()
  482. }
  483. // SharePermissions not required as cannot share with a user
  484. // worklogs
  485. gvs = gd.findByOr("Worklog", [author: sourceUser, updateauthor: sourceUser])
  486. msg = "Update ${gvs.size()} Worklog records"
  487. log.debug(msg)
  488. msgs << msg
  489. if (!preview) {
  490. gvs.each {GenericValue gv ->
  491. if (gv.get("author") == sourceUser) {
  492. gv.set("author", targetUser)
  493. }
  494. if (gv.get("updateauthor") == sourceUser) {
  495. gv.set("updateauthor", targetUser)
  496. }
  497. gv.store()
  498. }
  499. }
  500. // project and comp leads
  501. gvs = gd.findByAnd("Project", [lead: sourceUser])
  502. msg = "Update ${gvs.size()} Project records"
  503. log.debug(msg)
  504. msgs << msg
  505. if (!preview) {
  506. gvs.each {GenericValue gv ->
  507. gv.set("lead", targetUser)
  508. gv.store()
  509. }
  510. }
  511. gvs = gd.findByAnd("Component", [lead: sourceUser])
  512. msg = "Update ${gvs.size()} Component records"
  513. log.debug(msg)
  514. msgs << msg
  515. if (!preview) {
  516. gvs.each {GenericValue gv ->
  517. gv.set("lead", targetUser)
  518. gv.store()
  519. }
  520. }
  521. // notification schemes
  522. gvs = gd.findByAnd("Notification", [type: SingleUser.DESC, parameter: sourceUser])
  523. targetGvs = gd.findByAnd("Notification", [type: SingleUser.DESC, parameter: targetUser])
  524. msg = "Update ${gvs.size()} Notification records"
  525. log.debug(msg)
  526. msgs << msg
  527. if (!preview) {
  528. gvs.each {GenericValue gv ->
  529. if (doMerge && targetGvs.find {
  530. gv.getFields(["scheme", "eventTypeId", "type"]) == it.getFields(["scheme", "eventTypeId", "type"])
  531. }) {
  532. log.debug("Remove Notification record")
  533. gv.remove()
  534. }
  535. else {
  536. gv.set("parameter", targetUser)
  537. gv.store()
  538. }
  539. }
  540. notificationSchemeManager.flushProjectSchemes()
  541. }
  542. // permission schemes
  543. gvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: sourceUser])
  544. targetGvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: targetUser])
  545. msg = "Update ${gvs.size()} SchemePermissions records"
  546. log.debug(msg)
  547. msgs << msg
  548. if (!preview) {
  549. List matchFields = ['scheme', 'permission', 'type', 'parameter']
  550. gvs.each {GenericValue gv ->
  551. if (doMerge && targetGvs.find {
  552. gv.getFields(matchFields) == it.getFields(matchFields)
  553. }) {
  554. log.debug("Remove Notification record")
  555. gv.remove()
  556. }
  557. else {
  558. gv.set("parameter", targetUser)
  559. gv.store()
  560. }
  561. }
  562. defaultPermissionSchemeManager.flushProjectSchemes()
  563. defaultPermissionSchemeManager.flushSchemeEntities()
  564. }
  565. // issue security schemes
  566. gvs = gd.findByAnd("SchemeIssueSecurities", [type: "user", parameter: sourceUser])
  567. targetGvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: targetUser])
  568. msg = "Update ${gvs.size()} SchemeIssueSecurities records"
  569. log.debug(msg)
  570. msgs << msg
  571. if (!preview) {
  572. List matchFields = ['scheme', 'security', 'type', 'parameter']
  573. gvs.each {GenericValue gv ->
  574. if (doMerge && targetGvs.find {
  575. gv.getFields(matchFields) == it.getFields(matchFields)
  576. }) {
  577. log.debug("Remove Notification record")
  578. gv.remove()
  579. }
  580. else {
  581. gv.set("parameter", targetUser)
  582. gv.store()
  583. }
  584. }
  585. securitySchemeManager.flushProjectSchemes()
  586. }
  587. // user and multi-user custom fields
  588. gvs = gd.findByCondition("CustomField",
  589. new EntityExpr("customfieldtypekey", EntityOperator.IN,
  590. [
  591. "com.atlassian.jira.plugin.system.customfieldtypes:userpicker",
  592. "com.atlassian.jira.plugin.system.customfieldtypes:multiuserpicker"
  593. ])
  594. ,
  595. ["id"], Collections.<String> emptyList())
  596. Long cfValueKount = 0
  597. gvs.each {GenericValue gv ->
  598. gv.getRelatedByAnd("ChildCustomFieldValue", [stringvalue: sourceUser]).each {GenericValue cfGv ->
  599. cfValueKount++
  600. if (!preview) {
  601. cfGv.set("stringvalue", targetUser)
  602. cfGv.store()
  603. }
  604. reindexIssueIds.add(cfGv.getLong("issue"))
  605. }
  606. }
  607. msg = "Update $cfValueKount CustomFieldValue records"
  608. log.debug(msg)
  609. msgs << msg
  610. gvs = gd.findByAnd("OSHistoryStep", [caller: sourceUser])
  611. msg = "Update ${gvs.size()} OSHistoryStep records"
  612. log.debug(msg)
  613. msgs << msg
  614. if (!preview) {
  615. gvs.each {GenericValue gv ->
  616. gv.set("caller", targetUser)
  617. gv.store()
  618. }
  619. }
  620. // fileattachments
  621. gvs = gd.findByAnd("FileAttachment", [author: sourceUser])
  622. msg = "Update ${gvs.size()} FileAttachment records"
  623. log.debug(msg)
  624. msgs << msg
  625. if (!preview) {
  626. gvs.each {GenericValue gv ->
  627. gv.set("author", targetUser)
  628. gv.store()
  629. }
  630. }
  631. // -------------------------------------- Finalise --------------------------------------
  632. if (! preview) {
  633. log.debug("Commit transaction")
  634. CoreTransactionUtil.commit(began);
  635. // Only for 4.1 and above - clearing the other caches are not necessary above but kept there if
  636. // I decide to backport this
  637. log.debug("Publish clear cache event")
  638. ComponentManager.getComponentInstanceOfType(EventPublisher.class).publish(ClearCacheEvent.INSTANCE);
  639. // reindex issues
  640. reindexIssueIds.each {Long issueId ->
  641. reindexIssue(issueManager, issueId, log, indexManager);
  642. }
  643. UserManager.getInstance().flushCaches()
  644. User user = userManager.getUser(targetUser)
  645. user.getAccessProvider().flushCaches()
  646. user.getProfileProvider().flushCaches()
  647. }
  648. msg = "Reindex ${reindexIssueIds.size()} issues"
  649. log.debug(msg)
  650. msgs << msg
  651. }
  652. catch (Exception e) {
  653. log.error("Problem renaming user, rolling back")
  654. CoreTransactionUtil.rollback(began)
  655. throw e
  656. }
  657. finally {
  658. // not sure if we need anything here
  659. }
  660. }
  661. private List<EntityExpr> getUserAssociationConditionList(String userId) {
  662. List userAssocCList = [
  663. new EntityExpr("sourceName", EntityOperator.EQUALS, userId),
  664. new EntityExpr("associationType", EntityOperator.IN,
  665. [UserAssociationParser.ASSOCIATION_TYPE_WATCH_ISSUE, UserAssociationParser.ASSOCIATION_TYPE_VOTE_ISSUE]),
  666. ]
  667. return userAssocCList
  668. }
  669. def updateSingleRequest(SearchRequest sr, String sourceUser, String targetUser) {
  670. Query query = sr.getQuery()
  671. Query newQuery
  672. // A query may have no where clause and select everything... if so .accept dies
  673. if (query.getWhereClause()) {
  674. newQuery = transformSearchRequest(query, sourceUser, targetUser)
  675. }
  676. else {
  677. newQuery = query
  678. }
  679. if (query.whereClause.toString() != newQuery.whereClause.toString()) {
  680. // this is stupid but setting a queryString seems to blow away the whereClause, so we do this twice
  681. nSearchReqsUpdated++
  682. log.debug("Update sr name: " + sr.name)
  683. // log.debug("queryString: " + newQuery.queryString)
  684. // log.debug("newQuery: $newQuery")
  685. sr.setQuery(newQuery)
  686. JiraServiceContextImpl ctx = new JiraServiceContextImpl(userManager.getUser(sr.ownerUserName))
  687. searchRequestService.validateFilterForUpdate(ctx, sr)
  688. if (ctx.errorCollection.hasAnyErrors()) {
  689. log.error("errors: " + ctx.getErrorCollection())
  690. }
  691. else {
  692. if (! preview) {
  693. searchRequestService.updateFilter(ctx, sr)
  694. }
  695. }
  696. }
  697. else {
  698. log.debug("No need to update name: " + sr.name)
  699. }
  700. }
  701. private def updateAllSearchRequests(String sourceUser, String targetUser) {
  702. EnclosedIterable<SearchRequest> srs = searchRequestManager.getAll()
  703. srs.foreach(new Consumer<SearchRequest>() {
  704. void consume(SearchRequest sr) {
  705. updateSingleRequest(sr, sourceUser, targetUser)
  706. }
  707. })
  708. msg = "Update parameters for $nSearchReqsUpdated search requests"
  709. log.debug(msg)
  710. msgs << msg
  711. }
  712. String getDescription(Map params, boolean forPreview) {
  713. preview = true
  714. Map out = doScript(params)
  715. out["output"]
  716. }
  717. }
  718. private class RenamingClauseVisitor implements ClauseVisitor {
  719. Category log = Category.getInstance(RenamingClauseVisitor.class)
  720. SearchHandlerManager searchHandlerManager = ComponentManager.getComponentInstanceOfType(SearchHandlerManager.class) as SearchHandlerManager
  721. ComponentManager componentManager = ComponentManager.getInstance()
  722. CustomFieldManager customFieldManager = componentManager.getCustomFieldManager()
  723. String sourceUser
  724. String targetUser
  725. RenamingClauseVisitor(String sourceUser, String targetUser) {
  726. this.sourceUser = sourceUser
  727. this.targetUser = targetUser
  728. }
  729. Object visit(AndClause andClause) {
  730. Collection<Clause> newClauses = transformSubClauses(andClause)
  731. new AndClause(newClauses)
  732. }
  733. Object visit(NotClause notClause) {
  734. new NotClause(notClause.getSubClause().accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause)
  735. }
  736. Object visit(OrClause orClause) {
  737. Collection<Clause> newClauses = transformSubClauses(orClause)
  738. new OrClause(newClauses)
  739. }
  740. private Collection<Clause> transformSubClauses(Clause mainClause) {
  741. Collection<Clause> newClauses = []
  742. mainClause.getClauses().each {Clause clause ->
  743. newClauses.add(clause.accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause)
  744. }
  745. return newClauses
  746. }
  747. Object visit(TerminalClause clause) {
  748. final Collection<ClauseHandler> handlers = searchHandlerManager.getClauseHandler(clause.getName());
  749. Collection<String> fieldIds = searchHandlerManager.getFieldIds(clause.getName())
  750. // If there are multiple handers, eg field has multiple duplicate names, don't deal with it
  751. // ie multiple fields with the same name visible to this user
  752. if (fieldIds?.size() != 1) {
  753. return clause
  754. }
  755. String fieldId = fieldIds.toList().first()
  756. boolean isUserField = false
  757. if (fieldId.startsWith("customfield_")) {
  758. CustomFieldType cfType = customFieldManager.getCustomFieldObject(fieldId).getCustomFieldType()
  759. if (cfType instanceof UserField) {
  760. isUserField = true
  761. }
  762. }
  763. if (["reporter", "assignee"].contains(fieldId) || isUserField) {
  764. Operand operand = clause.getOperand()
  765. Operand newOp = operand.accept(new RenamingOperandVisitor(sourceUser, targetUser)) as Operand
  766. TerminalClauseImpl newTermClause = new TerminalClauseImpl(clause.name, clause.getOperator(), newOp)
  767. return newTermClause
  768. }
  769. clause
  770. }
  771. Object visit(WasClause wasClause) {
  772. // wasClause in 4.3 not handled at the moment
  773. wasClause
  774. }
  775. }
  776. private class RenamingOperandVisitor implements OperandVisitor {
  777. Category log = Category.getInstance(RenamingOperandVisitor.class)
  778. String sourceUser
  779. String targetUser
  780. RenamingOperandVisitor(String sourceUser, String targetUser) {
  781. this.sourceUser = sourceUser
  782. this.targetUser = targetUser
  783. }
  784. Object visit(EmptyOperand empty) {
  785. empty
  786. }
  787. Object visit(FunctionOperand function) {
  788. function
  789. }
  790. Object visit(MultiValueOperand multiValue) {
  791. List<Operand> newOps = multiValue.getValues().collectAll {Operand operand ->
  792. if (operand instanceof SingleValueOperand && operand.getStringValue() == sourceUser) {
  793. return new SingleValueOperand(targetUser)
  794. }
  795. operand
  796. }
  797. new MultiValueOperand(newOps)
  798. }
  799. Object visit(SingleValueOperand singleValueOperand) {
  800. if (singleValueOperand.getStringValue() == sourceUser) {
  801. return new SingleValueOperand(targetUser)
  802. }
  803. singleValueOperand
  804. }
  805. }