PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/sorin/scriptrunner-public-sorin
Groovy | 913 lines | 680 code | 109 blank | 124 comment | 118 complexity | 379122b5af1d3116f7a416ac5859e6eb 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. public class RenameUser implements CannedScript {
  58. Category log = Category.getInstance(RenameUser.class)
  59. UserManager userManager = UserManager.getInstance()
  60. ComponentManager componentManager = ComponentManager.getInstance()
  61. SearchRequestManager searchRequestManager = componentManager.getSearchRequestManager()
  62. SearchRequestService searchRequestService = componentManager.getSearchRequestService()
  63. public static final String FIELD_FROM_USER_ID = 'FIELD_FROM_USER_ID'
  64. public static final String FIELD_TO_USER_ID = 'FIELD_TO_USER_ID'
  65. public static final String FIELD_MERGE = 'FIELD_MERGE'
  66. List msgs = []
  67. String msg
  68. Set<Long> reindexIssueIds = new HashSet()
  69. boolean preview = false
  70. Long nSearchReqsUpdated = 0
  71. boolean doMerge
  72. public String getName() {
  73. "Renames a user ID"
  74. }
  75. public String getDescription() {
  76. "Changes a user's ID without reindexing or restarting"
  77. }
  78. List getCategories() {
  79. ["ADMIN"]
  80. }
  81. public Boolean isFinalParamsPage(Map params) {
  82. true
  83. }
  84. public List getParameters(Map params) {
  85. [
  86. [
  87. Name: FIELD_FROM_USER_ID,
  88. Label: 'From user ID',
  89. Description: "Rename or merge from who?",
  90. ],
  91. [
  92. Name: FIELD_TO_USER_ID,
  93. Label: 'To user ID',
  94. Description: "Rename or merge to who?",
  95. ],
  96. [
  97. Name: FIELD_MERGE,
  98. Label: 'Merge',
  99. Type: 'radio',
  100. Description: "Merge the first user to the second user, rather than rename",
  101. Values: ["false": "Rename", "true": "Merge"],
  102. ],
  103. ]
  104. }
  105. ErrorCollection doValidate(Map params, boolean forPreview) {
  106. ErrorCollection errorCollection = new SimpleErrorCollection()
  107. String sourceUserId = params[FIELD_FROM_USER_ID] as String
  108. String targetUserId = params[FIELD_TO_USER_ID] as String
  109. if (! params[FIELD_MERGE]) {
  110. errorCollection.addError(FIELD_MERGE, "Please select an option")
  111. }
  112. doMerge = Boolean.parseBoolean(params[FIELD_MERGE] as String)
  113. [FIELD_FROM_USER_ID, FIELD_TO_USER_ID].each {String p ->
  114. if (! params[p]) {
  115. errorCollection.addError(p, "Please provide the user ID")
  116. }
  117. }
  118. if (errorCollection.hasAnyErrors()) {
  119. return errorCollection
  120. }
  121. if (! UserUtils.existsUser(sourceUserId as String)) {
  122. errorCollection.addError(FIELD_FROM_USER_ID, "Cannot find user ID")
  123. }
  124. if (sourceUserId == targetUserId) {
  125. errorCollection.addErrorMessage("From and to user can't be the same")
  126. }
  127. boolean targetExists = UserUtils.existsUser(targetUserId as String)
  128. if (doMerge && ! targetExists) {
  129. errorCollection.addError(FIELD_TO_USER_ID, "Target user ID must exist already for a merge")
  130. }
  131. if (! doMerge && targetExists) {
  132. errorCollection.addError(FIELD_TO_USER_ID, "Target user ID must not exist already")
  133. }
  134. if (sourceUserId == componentManager.getJiraAuthenticationContext().getUser()?.name) {
  135. errorCollection.addError(FIELD_FROM_USER_ID, "You can't rename the logged in user")
  136. }
  137. errorCollection
  138. }
  139. Query transformSearchRequest(Query query, String sourceUser, String targetUser) {
  140. Clause whereClause = query.getWhereClause()
  141. Clause newWhereClause = whereClause.accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause
  142. // log.debug("original whereClause: " + query.whereClause.toString())
  143. // log.debug "new where clause: " + newWhereClause.toString()
  144. Query newQuery = new QueryImpl(newWhereClause)
  145. newQuery = new QueryImpl(newWhereClause, query.orderByClause, newQuery.queryString)
  146. newQuery
  147. }
  148. Map doScript(Map params) {
  149. String sourceUser = params[FIELD_FROM_USER_ID] as String
  150. String targetUser = params[FIELD_TO_USER_ID] as String
  151. doMerge = Boolean.parseBoolean(params[FIELD_MERGE] as String)
  152. // make sure they're not logged on??
  153. if (!preview) {
  154. msgs << "<b>Completed</b><br>"
  155. }
  156. updateGenericValues(sourceUser, targetUser)
  157. // updateAllSearchRequests(sourceUser, targetUser)
  158. if (!preview) {
  159. msg = "Completed ${doMerge ? "merge" : "rename"} of $sourceUser to $targetUser"
  160. log.warn(msg)
  161. msgs << msg
  162. }
  163. params["output"] = "<pre>" + msgs.join("\n") + "<pre>"
  164. params
  165. }
  166. private def reindexIssue(IssueManager issueManager, Long issueId, Category log, IssueIndexManager indexManager) {
  167. GenericValue issue = issueManager.getIssue(issueId)
  168. boolean wasIndexing = ImportUtils.isIndexIssues();
  169. ImportUtils.setIndexIssues(true);
  170. if ((BuildUtils.getCurrentBuildNumber() as Long) < 614) {
  171. ManagerFactory.getCacheManager().flush(com.atlassian.jira.issue.cache.CacheManager.ISSUE_CACHE, issue)
  172. }
  173. log.debug("Reindex issue ${issue.key}")
  174. indexManager.reIndex(issue);
  175. ImportUtils.setIndexIssues(wasIndexing)
  176. }
  177. def updateGenericValues(String sourceUser, String targetUser) {
  178. boolean began = false
  179. DelegatorInterface gd = (DelegatorInterface) componentManager.getComponentInstanceOfType(DelegatorInterface.class)
  180. IssueIndexManager indexManager = ComponentManager.getInstance().getIndexManager()
  181. CachingProjectRoleAndActorStore cachingProjectRoleAndActorStore = ComponentManager.getComponentInstanceOfType(CachingProjectRoleAndActorStore.class) as CachingProjectRoleAndActorStore
  182. DefaultNotificationSchemeManager notificationSchemeManager = ComponentManager.getComponentInstanceOfType(DefaultNotificationSchemeManager.class) as DefaultNotificationSchemeManager
  183. DefaultPermissionSchemeManager defaultPermissionSchemeManager = ComponentManager.getComponentInstanceOfType(DefaultPermissionSchemeManager.class) as DefaultPermissionSchemeManager
  184. IssueSecuritySchemeManagerImpl securitySchemeManager = ComponentManager.getComponentInstanceOfType(IssueSecuritySchemeManagerImpl.class) as IssueSecuritySchemeManagerImpl
  185. CachingSearchRequestStore cachingSearchRequestStore = ComponentManager.getComponentInstanceOfType(CachingSearchRequestStore.class) as CachingSearchRequestStore
  186. IssueManager issueManager = componentManager.getIssueManager()
  187. if (! preview) {
  188. log.debug("Begin transaction")
  189. began = CoreTransactionUtil.begin();
  190. }
  191. try {
  192. msg = "Beginning ${doMerge ? "merge" : "rename"} of $sourceUser to $targetUser"
  193. log.warn(msg)
  194. msgs << msg
  195. // Rename the user record itself
  196. if ((BuildUtils.getCurrentBuildNumber() as Long) < 614) {
  197. List gvs = gd.findByAnd("OSUser", [name: sourceUser])
  198. assert gvs.size() == 1
  199. if (! preview) {
  200. GenericValue usergv = gvs.first() as GenericValue
  201. if (doMerge) {
  202. // todo: delete their associated property records
  203. usergv.remove()
  204. }
  205. else {
  206. usergv.set("name", targetUser)
  207. usergv.store()
  208. }
  209. }
  210. // group memberships
  211. gvs = gd.findByAnd("OSMembership", [userName: sourceUser])
  212. msg = "Update ${gvs.size()} OSMembership records"
  213. log.debug(msg)
  214. msgs << msg
  215. if (!preview) {
  216. List<GenericValue> targetGvs = gd.findByAnd("OSMembership", [userName: targetUser])
  217. List<Object> targetGroups = targetGvs*.get("groupName")
  218. gvs.each {GenericValue gv ->
  219. if (doMerge && targetGroups.contains (gv.get("groupName"))) {
  220. gv.remove()
  221. }
  222. else {
  223. gv.set("userName", targetUser)
  224. gv.store()
  225. }
  226. }
  227. }
  228. }
  229. else {
  230. // todo: only look in jira internal directory
  231. List gvs = gd.findByAnd("User", [userName: sourceUser])
  232. assert gvs.size() == 1
  233. if (! preview) {
  234. GenericValue usergv = gvs.first() as GenericValue
  235. if (doMerge) {
  236. usergv.remove()
  237. }
  238. else {
  239. usergv.set("userName", targetUser)
  240. usergv.set("lowerUserName", targetUser.toLowerCase())
  241. usergv.store()
  242. }
  243. }
  244. // group memberships
  245. gvs = gd.findByAnd("Membership", [childName: sourceUser, directoryId: 1, membershipType: "GROUP_USER"])
  246. msg = "Update ${gvs.size()} Membership records"
  247. log.debug(msg)
  248. msgs << msg
  249. if (!preview) {
  250. List<GenericValue> targetGvs = gd.findByAnd("Membership", [childName: targetUser, directoryId: 1, membershipType: "GROUP_USER"])
  251. List<Object> targetGroups = targetGvs*.get("parentName")
  252. gvs.each {GenericValue gv ->
  253. if (doMerge && targetGroups.contains (gv.get("parentName"))) {
  254. gv.remove()
  255. }
  256. else {
  257. gv.set("childName", targetUser)
  258. gv.set("lowerChildName", targetUser)
  259. gv.store()
  260. }
  261. }
  262. }
  263. }
  264. // trustedapp
  265. List gvs = gd.findByAnd("TrustedApplication", [createdBy: sourceUser])
  266. msg = "Update ${gvs.size()} TrustedApplication records"
  267. log.debug(msg)
  268. msgs << msg
  269. if (!preview) {
  270. gvs.each {GenericValue gv ->
  271. gv.set("createdBy", targetUser)
  272. gv.store()
  273. }
  274. }
  275. // SearchRequest ownership
  276. // if this is a merge, we may get dupe filter names. Not ideal but handled OK by the UI
  277. gvs = gd.findByOr("SearchRequest", [user: sourceUser, author: sourceUser])
  278. msg = "Update ${gvs.size()} SearchRequest records"
  279. log.debug(msg)
  280. msgs << msg
  281. if (!preview) {
  282. gvs.each {GenericValue gv ->
  283. if (gv.get("user")) {
  284. gv.set("user", targetUser)
  285. }
  286. if (gv.get("author")) {
  287. gv.set("author", targetUser)
  288. }
  289. cachingSearchRequestStore.removeFromCache(gv.getLong("id"))
  290. gv.store()
  291. }
  292. }
  293. // ColumnLayout - choose one or tother.
  294. gvs = gd.findByAnd("ColumnLayout", [username: sourceUser])
  295. msg = "Update ${gvs.size()} ColumnLayout records"
  296. List<GenericValue> targetGvs = gd.findByAnd("ColumnLayout", [username: targetUser])
  297. log.debug(msg)
  298. msgs << msg
  299. if (!preview) {
  300. gvs.each {GenericValue gv ->
  301. if (doMerge && targetGvs.find {it.getLong("searchrequest") == gv.getLong("searchrequest")}) {
  302. gd.removeRelated("ChildColumnLayoutItem", gv)
  303. gv.remove()
  304. }
  305. else {
  306. gv.set("username", targetUser)
  307. gv.store()
  308. }
  309. }
  310. }
  311. if (!doMerge) {
  312. gvs = gd.findByAnd("UserHistoryItem", [username: sourceUser])
  313. msg = "Update ${gvs.size()} UserHistoryItem records"
  314. log.debug(msg)
  315. msgs << msg
  316. if (!preview) {
  317. gvs.each {GenericValue gv ->
  318. gv.set("username", targetUser)
  319. gv.store()
  320. }
  321. }
  322. }
  323. gvs = gd.findByAnd("FavouriteAssociations", [username: sourceUser])
  324. targetGvs = gd.findByAnd("FavouriteAssociations", [username: targetUser])
  325. msg = "Update ${gvs.size()} FavouriteAssociations records"
  326. log.debug(msg)
  327. msgs << msg
  328. if (!preview) {
  329. List matchFields = ["entityType", "entityId"]
  330. gvs.each {GenericValue gv ->
  331. // favourite assocs, only fave if not already faved
  332. if (doMerge && targetGvs.find {
  333. it.getFields(matchFields) == gv.getFields(matchFields)
  334. }) {
  335. // favcount must be adjusted downwards when both are favouriting
  336. log.debug("Remove fav association record and decrement favourite count")
  337. GenericValue sharedEntityGv = gd.findByPrimaryKey(gv.get("entityType") as String, [id: gv.get("entityId")])
  338. sharedEntityGv.set("favCount", (sharedEntityGv.get("favCount") as Long) - 1)
  339. sharedEntityGv.store()
  340. gv.remove()
  341. }
  342. else {
  343. gv.set("username", targetUser)
  344. gv.store()
  345. }
  346. }
  347. }
  348. ["PortalPage", "FilterSubscription"].each {String entityName ->
  349. gvs = gd.findByAnd(entityName, [username: sourceUser])
  350. msg = "Update ${gvs.size()} $entityName records"
  351. log.debug(msg)
  352. msgs << msg
  353. if (!preview) {
  354. gvs.each {GenericValue gv ->
  355. gv.set("username", targetUser)
  356. gv.store()
  357. }
  358. }
  359. }
  360. // note: for versions prior to 4.1 the favouritesStoreCache needs to be flushed
  361. // Core issue fields
  362. gvs = gd.findByOr("Issue", [reporter: sourceUser, assignee: sourceUser])
  363. msg = "Update ${gvs.size()} Issue records"
  364. log.debug(msg)
  365. msgs << msg
  366. gvs.each {GenericValue gv ->
  367. if (!preview) {
  368. if (gv.get("reporter") == sourceUser) {
  369. gv.set("reporter", targetUser)
  370. }
  371. if (gv.get("assignee") == sourceUser) {
  372. gv.set("assignee", targetUser)
  373. }
  374. gv.store()
  375. }
  376. reindexIssueIds.add(gv.getLong("id"))
  377. }
  378. // issue transition history
  379. gvs = gd.findByOr("Action", [author: sourceUser, updateauthor: sourceUser])
  380. msg = "Update ${gvs.size()} Action records"
  381. log.debug(msg)
  382. msgs << msg
  383. if (!preview) {
  384. gvs.each {GenericValue gv ->
  385. if (gv.get("author") == sourceUser) {
  386. gv.set("author", targetUser)
  387. }
  388. if (gv.get("updateauthor") == sourceUser) {
  389. gv.set("updateauthor", targetUser)
  390. }
  391. gv.store()
  392. }
  393. }
  394. // change groups
  395. // gvs = gd.findByAnd("ChangeGroup", [author: sourceUser])
  396. // msg = "Update ${gvs.size()} ChangeGroup records"
  397. // log.debug(msg)
  398. // msgs << msg
  399. // if (!preview) {
  400. // gvs.each {GenericValue gv ->
  401. // gv.set("author", targetUser)
  402. // gv.store()
  403. // }
  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. /*
  589. gvs = gd.findByCondition("CustomField",
  590. new EntityExpr("customfieldtypekey", EntityOperator.IN,
  591. [
  592. "com.atlassian.jira.plugin.system.customfieldtypes:userpicker",
  593. "com.atlassian.jira.plugin.system.customfieldtypes:multiuserpicker"
  594. ])
  595. ,
  596. ["id"], Collections.<String> emptyList())
  597. Long cfValueKount = 0
  598. gvs.each {GenericValue gv ->
  599. gv.getRelatedByAnd("ChildCustomFieldValue", [stringvalue: sourceUser]).each {GenericValue cfGv ->
  600. cfValueKount++
  601. if (!preview) {
  602. cfGv.set("stringvalue", targetUser)
  603. cfGv.store()
  604. }
  605. reindexIssueIds.add(cfGv.getLong("issue"))
  606. }
  607. }
  608. msg = "Update $cfValueKount CustomFieldValue records"
  609. log.debug(msg)
  610. msgs << msg
  611. gvs = gd.findByAnd("OSHistoryStep", [caller: sourceUser])
  612. msg = "Update ${gvs.size()} OSHistoryStep records"
  613. log.debug(msg)
  614. msgs << msg
  615. if (!preview) {
  616. gvs.each {GenericValue gv ->
  617. gv.set("caller", targetUser)
  618. gv.store()
  619. }
  620. }
  621. */
  622. // fileattachments
  623. gvs = gd.findByAnd("FileAttachment", [author: sourceUser])
  624. msg = "Update ${gvs.size()} FileAttachment records"
  625. log.debug(msg)
  626. msgs << msg
  627. if (!preview) {
  628. gvs.each {GenericValue gv ->
  629. gv.set("author", targetUser)
  630. gv.store()
  631. }
  632. }
  633. // -------------------------------------- Finalise --------------------------------------
  634. if (! preview) {
  635. log.debug("Commit transaction")
  636. CoreTransactionUtil.commit(began);
  637. // Only for 4.1 and above - clearing the other caches are not necessary above but kept there if
  638. // I decide to backport this
  639. log.debug("Publish clear cache event")
  640. // ComponentManager.getComponentInstanceOfType(EventPublisher.class).publish(ClearCacheEvent.INSTANCE);
  641. // reindex issues
  642. // reindexIssueIds.each {Long issueId ->
  643. // reindexIssue(issueManager, issueId, log, indexManager);
  644. // }
  645. // UserManager.getInstance().flushCaches()
  646. // User user = userManager.getUser(targetUser)
  647. // user.getAccessProvider().flushCaches()
  648. // user.getProfileProvider().flushCaches()
  649. }
  650. msg = "Reindex ${reindexIssueIds.size()} issues"
  651. log.debug(msg)
  652. msgs << msg
  653. }
  654. catch (Exception e) {
  655. log.error("Problem renaming user, rolling back")
  656. CoreTransactionUtil.rollback(began)
  657. throw e
  658. }
  659. finally {
  660. // not sure if we need anything here
  661. }
  662. }
  663. private List<EntityExpr> getUserAssociationConditionList(String userId) {
  664. List userAssocCList = [
  665. new EntityExpr("sourceName", EntityOperator.EQUALS, userId),
  666. new EntityExpr("associationType", EntityOperator.IN,
  667. [UserAssociationParser.ASSOCIATION_TYPE_WATCH_ISSUE, UserAssociationParser.ASSOCIATION_TYPE_VOTE_ISSUE]),
  668. ]
  669. return userAssocCList
  670. }
  671. def updateSingleRequest(SearchRequest sr, String sourceUser, String targetUser) {
  672. Query query = sr.getQuery()
  673. Query newQuery
  674. // A query may have no where clause and select everything... if so .accept dies
  675. if (query.getWhereClause()) {
  676. newQuery = transformSearchRequest(query, sourceUser, targetUser)
  677. }
  678. else {
  679. newQuery = query
  680. }
  681. if (query.whereClause.toString() != newQuery.whereClause.toString()) {
  682. // this is stupid but setting a queryString seems to blow away the whereClause, so we do this twice
  683. nSearchReqsUpdated++
  684. log.debug("Update sr name: " + sr.name)
  685. // log.debug("queryString: " + newQuery.queryString)
  686. // log.debug("newQuery: $newQuery")
  687. sr.setQuery(newQuery)
  688. JiraServiceContextImpl ctx = new JiraServiceContextImpl(userManager.getUser(sr.ownerUserName))
  689. searchRequestService.validateFilterForUpdate(ctx, sr)
  690. if (ctx.errorCollection.hasAnyErrors()) {
  691. log.error("errors: " + ctx.getErrorCollection())
  692. }
  693. else {
  694. if (! preview) {
  695. searchRequestService.updateFilter(ctx, sr)
  696. }
  697. }
  698. }
  699. else {
  700. log.debug("No need to update name: " + sr.name)
  701. }
  702. }
  703. private def updateAllSearchRequests(String sourceUser, String targetUser) {
  704. EnclosedIterable<SearchRequest> srs = searchRequestManager.getAll()
  705. srs.foreach(new Consumer<SearchRequest>() {
  706. void consume(SearchRequest sr) {
  707. updateSingleRequest(sr, sourceUser, targetUser)
  708. }
  709. })
  710. msg = "Update parameters for $nSearchReqsUpdated search requests"
  711. log.debug(msg)
  712. msgs << msg
  713. }
  714. String getDescription(Map params, boolean forPreview) {
  715. preview = true
  716. Map out = doScript(params)
  717. out["output"]
  718. }
  719. }
  720. private class RenamingClauseVisitor implements ClauseVisitor {
  721. Category log = Category.getInstance(RenamingClauseVisitor.class)
  722. SearchHandlerManager searchHandlerManager = ComponentManager.getComponentInstanceOfType(SearchHandlerManager.class) as SearchHandlerManager
  723. ComponentManager componentManager = ComponentManager.getInstance()
  724. CustomFieldManager customFieldManager = componentManager.getCustomFieldManager()
  725. String sourceUser
  726. String targetUser
  727. RenamingClauseVisitor(String sourceUser, String targetUser) {
  728. this.sourceUser = sourceUser
  729. this.targetUser = targetUser
  730. }
  731. Object visit(AndClause andClause) {
  732. Collection<Clause> newClauses = transformSubClauses(andClause)
  733. new AndClause(newClauses)
  734. }
  735. Object visit(NotClause notClause) {
  736. new NotClause(notClause.getSubClause().accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause)
  737. }
  738. Object visit(OrClause orClause) {
  739. Collection<Clause> newClauses = transformSubClauses(orClause)
  740. new OrClause(newClauses)
  741. }
  742. private Collection<Clause> transformSubClauses(Clause mainClause) {
  743. Collection<Clause> newClauses = []
  744. mainClause.getClauses().each {Clause clause ->
  745. newClauses.add(clause.accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause)
  746. }
  747. return newClauses
  748. }
  749. Object visit(TerminalClause clause) {
  750. final Collection<ClauseHandler> handlers = searchHandlerManager.getClauseHandler(clause.getName());
  751. Collection<String> fieldIds = searchHandlerManager.getFieldIds(clause.getName())
  752. // If there are multiple handers, eg field has multiple duplicate names, don't deal with it
  753. // ie multiple fields with the same name visible to this user
  754. if (fieldIds?.size() != 1) {
  755. return clause
  756. }
  757. String fieldId = fieldIds.toList().first()
  758. boolean isUserField = false
  759. if (fieldId.startsWith("customfield_")) {
  760. CustomFieldType cfType = customFieldManager.getCustomFieldObject(fieldId).getCustomFieldType()
  761. if (cfType instanceof UserField) {
  762. isUserField = true
  763. }
  764. }
  765. if (["reporter", "assignee"].contains(fieldId) || isUserField) {
  766. Operand operand = clause.getOperand()
  767. Operand newOp = operand.accept(new RenamingOperandVisitor(sourceUser, targetUser)) as Operand
  768. TerminalClauseImpl newTermClause = new TerminalClauseImpl(clause.name, clause.getOperator(), newOp)
  769. return newTermClause
  770. }
  771. clause
  772. }
  773. }
  774. private class RenamingOperandVisitor implements OperandVisitor {
  775. Category log = Category.getInstance(RenamingOperandVisitor.class)
  776. String sourceUser
  777. String targetUser
  778. RenamingOperandVisitor(String sourceUser, String targetUser) {
  779. this.sourceUser = sourceUser
  780. this.targetUser = targetUser
  781. }
  782. Object visit(EmptyOperand empty) {
  783. empty
  784. }
  785. Object visit(FunctionOperand function) {
  786. function
  787. }
  788. Object visit(MultiValueOperand multiValue) {
  789. List<Operand> newOps = multiValue.getValues().collectAll {Operand operand ->
  790. if (operand instanceof SingleValueOperand && operand.getStringValue() == sourceUser) {
  791. return new SingleValueOperand(targetUser)
  792. }
  793. operand
  794. }
  795. new MultiValueOperand(newOps)
  796. }
  797. Object visit(SingleValueOperand singleValueOperand) {
  798. if (singleValueOperand.getStringValue() == sourceUser) {
  799. return new SingleValueOperand(targetUser)
  800. }
  801. singleValueOperand
  802. }
  803. }