PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/sorin/scriptrunner-public-sorin
Groovy | 928 lines | 756 code | 114 blank | 58 comment | 120 complexity | 34b729fe2b2d6a6bd713c42e3764e8be 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 org.apache.log4j.Category
  6. import com.atlassian.query.clause.Clause
  7. import com.atlassian.query.clause.TerminalClauseImpl
  8. import com.atlassian.query.operand.Operand
  9. import com.atlassian.query.operand.SingleValueOperand
  10. import com.atlassian.query.clause.OrClause
  11. import com.atlassian.jira.issue.search.SearchRequestManager
  12. import com.atlassian.jira.issue.search.SearchRequest
  13. import com.atlassian.jira.bc.filter.SearchRequestService
  14. import com.atlassian.jira.util.collect.EnclosedIterable
  15. import com.atlassian.jira.util.Consumer
  16. import com.atlassian.query.clause.ClauseVisitor
  17. import com.atlassian.query.clause.AndClause
  18. import com.atlassian.query.clause.NotClause
  19. import com.atlassian.query.clause.TerminalClause
  20. import com.atlassian.query.operand.OperandVisitor
  21. import com.atlassian.query.operand.EmptyOperand
  22. import com.atlassian.query.operand.FunctionOperand
  23. import com.atlassian.query.operand.MultiValueOperand
  24. import com.atlassian.query.Query
  25. import com.atlassian.query.QueryImpl
  26. import com.atlassian.jira.issue.search.managers.SearchHandlerManager
  27. import com.atlassian.jira.jql.ClauseHandler
  28. import com.atlassian.jira.issue.CustomFieldManager
  29. import com.atlassian.jira.issue.customfields.CustomFieldType
  30. import com.atlassian.jira.issue.fields.UserField
  31. import com.atlassian.jira.bc.JiraServiceContextImpl
  32. import com.atlassian.core.ofbiz.util.CoreTransactionUtil
  33. import com.atlassian.jira.event.ClearCacheEvent
  34. import com.atlassian.event.api.EventPublisher
  35. import com.atlassian.jira.imports.project.parser.UserAssociationParser
  36. import org.ofbiz.core.entity.GenericValue
  37. import com.atlassian.jira.util.ImportUtils
  38. import com.atlassian.jira.issue.index.IssueIndexManager
  39. import com.atlassian.jira.ManagerFactory
  40. import com.atlassian.jira.issue.IssueManager
  41. import org.ofbiz.core.entity.DelegatorInterface
  42. import org.ofbiz.core.entity.EntityOperator
  43. import org.ofbiz.core.entity.EntityExpr
  44. import org.ofbiz.core.entity.EntityConditionList
  45. import com.atlassian.jira.security.roles.actor.UserRoleActorFactory
  46. import com.atlassian.jira.security.roles.CachingProjectRoleAndActorStore
  47. import com.atlassian.jira.notification.type.SingleUser
  48. import com.atlassian.jira.issue.security.IssueSecuritySchemeManagerImpl
  49. import com.atlassian.jira.permission.DefaultPermissionSchemeManager
  50. import com.atlassian.jira.notification.DefaultNotificationSchemeManager
  51. import com.atlassian.jira.issue.search.CachingSearchRequestStore
  52. import com.atlassian.jira.util.SimpleErrorCollection
  53. import com.atlassian.jira.util.BuildUtils
  54. import com.atlassian.query.clause.WasClause
  55. import com.atlassian.jira.user.util.UserUtil
  56. import com.atlassian.query.clause.ChangedClause
  57. public class RenameUser implements CannedScript {
  58. Category log = Category.getInstance(RenameUser.class)
  59. ComponentManager componentManager = ComponentManager.getInstance()
  60. UserUtil userUtil = componentManager.getUserUtil()
  61. SearchRequestManager searchRequestManager = componentManager.getSearchRequestManager()
  62. def 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 (! userUtil.getUser(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 = userUtil.getUser(targetUserId as String) as Boolean
  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. if (! preview) {
  199. gvs.each { GenericValue usergv ->
  200. if (doMerge) {
  201. // todo: delete their associated property records
  202. usergv.remove()
  203. }
  204. else {
  205. usergv.set("name", targetUser)
  206. usergv.store()
  207. }
  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. gvs.each {GenericValue usergv ->
  233. if (! preview) {
  234. if (doMerge) {
  235. usergv.remove()
  236. }
  237. else {
  238. usergv.set("userName", targetUser)
  239. usergv.set("lowerUserName", targetUser.toLowerCase())
  240. usergv.store()
  241. }
  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. // Change items
  406. // todo: custom field history items - problem is they are referenced by name which is not unique
  407. EntityConditionList conditionList = new EntityConditionList(
  408. [new EntityConditionList(
  409. [
  410. // Stoopid oracle can't do equals on a clob, LIKE shd be OK
  411. // alternative is to do the filtering in the code
  412. new EntityExpr("oldvalue", EntityOperator.LIKE, sourceUser),
  413. new EntityExpr("newvalue", EntityOperator.LIKE, sourceUser),
  414. ], EntityOperator.OR),
  415. new EntityConditionList([new EntityExpr("field", EntityOperator.IN, ["reporter", "assignee"])], EntityOperator.AND)
  416. ], EntityOperator.AND
  417. )
  418. gvs = gd.findByCondition("ChangeItem", conditionList, ["id", "field", "oldvalue", "newvalue"], Collections.<String> emptyList())
  419. msg = "Update ${gvs.size()} ChangeItem records"
  420. log.debug(msg)
  421. msgs << msg
  422. if (!preview) {
  423. gvs.each {GenericValue gv ->
  424. if (gv.get("oldvalue") == sourceUser) {
  425. gv.set("oldvalue", targetUser)
  426. }
  427. if (gv.get("newvalue") == sourceUser) {
  428. gv.set("newvalue", targetUser)
  429. }
  430. gv.store()
  431. }
  432. }
  433. // Watchers and voters
  434. gvs = gd.findByAnd("UserAssociation", getUserAssociationConditionList(sourceUser))
  435. targetGvs = gd.findByAnd("UserAssociation", getUserAssociationConditionList(targetUser))
  436. msg = "Update ${gvs.size()} UserAssociation records"
  437. log.debug(msg)
  438. msgs << msg
  439. gvs.each {GenericValue gv ->
  440. if (gv.get("sinkNodeEntity") == "Issue") {
  441. reindexIssueIds.add(gv.getLong("sinkNodeId"))
  442. }
  443. if (!preview) {
  444. gd.removeValue(gv)
  445. if (doMerge && targetGvs.find {
  446. it.get("sinkNodeId") == gv.get("sinkNodeId") &&
  447. it.get("sinkNodeEntity") == gv.get("sinkNodeEntity") &&
  448. it.get("associationType") == gv.get("associationType")
  449. }) {
  450. // do nothing as always remove
  451. log.debug("Removed userassoc record")
  452. }
  453. else {
  454. // gv.store() doesn't work because we're changing the primary key
  455. gv.set("sourceName", targetUser)
  456. gd.create(gv)
  457. }
  458. }
  459. }
  460. // roles
  461. gvs = gd.findByAnd("ProjectRoleActor", [roletype: UserRoleActorFactory.TYPE, roletypeparameter: sourceUser])
  462. targetGvs = gd.findByAnd("ProjectRoleActor", [roletype: UserRoleActorFactory.TYPE, roletypeparameter: targetUser])
  463. msg = "Update ${gvs.size()} ProjectRoleActor records"
  464. log.debug(msg)
  465. msgs << msg
  466. if (!preview) {
  467. gvs.each {GenericValue gv ->
  468. List matchFields = ["pid", "projectroleid"]
  469. /*
  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. */
  478. // we do always want to merge
  479. gv.set("roletypeparameter", targetUser)
  480. gv.store()
  481. //}
  482. }
  483. cachingProjectRoleAndActorStore.clearCaches()
  484. }
  485. // SharePermissions not required as cannot share with a user
  486. // worklogs
  487. gvs = gd.findByOr("Worklog", [author: sourceUser, updateauthor: sourceUser])
  488. msg = "Update ${gvs.size()} Worklog records"
  489. log.debug(msg)
  490. msgs << msg
  491. if (!preview) {
  492. gvs.each {GenericValue gv ->
  493. if (gv.get("author") == sourceUser) {
  494. gv.set("author", targetUser)
  495. }
  496. if (gv.get("updateauthor") == sourceUser) {
  497. gv.set("updateauthor", targetUser)
  498. }
  499. gv.store()
  500. }
  501. }
  502. // project and comp leads
  503. gvs = gd.findByAnd("Project", [lead: sourceUser])
  504. msg = "Update ${gvs.size()} Project records"
  505. log.debug(msg)
  506. msgs << msg
  507. if (!preview) {
  508. gvs.each {GenericValue gv ->
  509. gv.set("lead", targetUser)
  510. gv.store()
  511. }
  512. }
  513. gvs = gd.findByAnd("Component", [lead: sourceUser])
  514. msg = "Update ${gvs.size()} Component records"
  515. log.debug(msg)
  516. msgs << msg
  517. if (!preview) {
  518. gvs.each {GenericValue gv ->
  519. gv.set("lead", targetUser)
  520. gv.store()
  521. }
  522. }
  523. // notification schemes
  524. gvs = gd.findByAnd("Notification", [type: SingleUser.DESC, parameter: sourceUser])
  525. targetGvs = gd.findByAnd("Notification", [type: SingleUser.DESC, parameter: targetUser])
  526. msg = "Update ${gvs.size()} Notification records"
  527. log.debug(msg)
  528. msgs << msg
  529. if (!preview) {
  530. gvs.each {GenericValue gv ->
  531. if (doMerge && targetGvs.find {
  532. gv.getFields(["scheme", "eventTypeId", "type"]) == it.getFields(["scheme", "eventTypeId", "type"])
  533. }) {
  534. log.debug("Remove Notification record")
  535. gv.remove()
  536. }
  537. else {
  538. gv.set("parameter", targetUser)
  539. gv.store()
  540. }
  541. }
  542. notificationSchemeManager.flushProjectSchemes()
  543. }
  544. // permission schemes
  545. gvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: sourceUser])
  546. targetGvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: targetUser])
  547. msg = "Update ${gvs.size()} SchemePermissions records"
  548. log.debug(msg)
  549. msgs << msg
  550. if (!preview) {
  551. List matchFields = ['scheme', 'permission', 'type', 'parameter']
  552. gvs.each {GenericValue gv ->
  553. if (doMerge && targetGvs.find {
  554. gv.getFields(matchFields) == it.getFields(matchFields)
  555. }) {
  556. log.debug("Remove Notification record")
  557. gv.remove()
  558. }
  559. else {
  560. gv.set("parameter", targetUser)
  561. gv.store()
  562. }
  563. }
  564. defaultPermissionSchemeManager.flushProjectSchemes()
  565. defaultPermissionSchemeManager.flushSchemeEntities()
  566. }
  567. // issue security schemes
  568. gvs = gd.findByAnd("SchemeIssueSecurities", [type: "user", parameter: sourceUser])
  569. targetGvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: targetUser])
  570. msg = "Update ${gvs.size()} SchemeIssueSecurities records"
  571. log.debug(msg)
  572. msgs << msg
  573. if (!preview) {
  574. List matchFields = ['scheme', 'security', 'type', 'parameter']
  575. gvs.each {GenericValue gv ->
  576. if (doMerge && targetGvs.find {
  577. gv.getFields(matchFields) == it.getFields(matchFields)
  578. }) {
  579. log.debug("Remove Notification record")
  580. gv.remove()
  581. }
  582. else {
  583. gv.set("parameter", targetUser)
  584. gv.store()
  585. }
  586. }
  587. securitySchemeManager.flushProjectSchemes()
  588. }
  589. // user and multi-user custom fields
  590. gvs = gd.findByCondition("CustomField",
  591. new EntityExpr("customfieldtypekey", EntityOperator.IN,
  592. [
  593. "com.atlassian.jira.plugin.system.customfieldtypes:userpicker",
  594. "com.atlassian.jira.plugin.system.customfieldtypes:multiuserpicker"
  595. ])
  596. ,
  597. ["id"], Collections.<String> emptyList())
  598. Long cfValueKount = 0
  599. gvs.each {GenericValue gv ->
  600. gv.getRelatedByAnd("ChildCustomFieldValue", [stringvalue: sourceUser]).each {GenericValue cfGv ->
  601. cfValueKount++
  602. if (!preview) {
  603. cfGv.set("stringvalue", targetUser)
  604. cfGv.store()
  605. }
  606. reindexIssueIds.add(cfGv.getLong("issue"))
  607. }
  608. }
  609. msg = "Update $cfValueKount CustomFieldValue records"
  610. log.debug(msg)
  611. msgs << msg
  612. gvs = gd.findByAnd("OSHistoryStep", [caller: sourceUser])
  613. msg = "Update ${gvs.size()} OSHistoryStep records"
  614. log.debug(msg)
  615. msgs << msg
  616. if (!preview) {
  617. gvs.each {GenericValue gv ->
  618. gv.set("caller", targetUser)
  619. gv.store()
  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. }
  646. msg = "Reindex ${reindexIssueIds.size()} issues"
  647. log.debug(msg)
  648. msgs << msg
  649. }
  650. catch (Exception e) {
  651. log.error("Problem renaming user, rolling back")
  652. CoreTransactionUtil.rollback(began)
  653. throw e
  654. }
  655. finally {
  656. // not sure if we need anything here
  657. }
  658. }
  659. private List<EntityExpr> getUserAssociationConditionList(String userId) {
  660. List userAssocCList = [
  661. new EntityExpr("sourceName", EntityOperator.EQUALS, userId),
  662. new EntityExpr("associationType", EntityOperator.IN,
  663. [UserAssociationParser.ASSOCIATION_TYPE_WATCH_ISSUE, UserAssociationParser.ASSOCIATION_TYPE_VOTE_ISSUE]),
  664. ]
  665. return userAssocCList
  666. }
  667. def updateSingleRequest(SearchRequest sr, String sourceUser, String targetUser) {
  668. Query query = sr.getQuery()
  669. Query newQuery
  670. // A query may have no where clause and select everything... if so .accept dies
  671. if (query.getWhereClause()) {
  672. newQuery = transformSearchRequest(query, sourceUser, targetUser)
  673. }
  674. else {
  675. newQuery = query
  676. }
  677. if (query.whereClause.toString() != newQuery.whereClause.toString()) {
  678. // this is stupid but setting a queryString seems to blow away the whereClause, so we do this twice
  679. nSearchReqsUpdated++
  680. log.debug("Update sr name: " + sr.name)
  681. // log.debug("queryString: " + newQuery.queryString)
  682. // log.debug("newQuery: $newQuery")
  683. sr.setQuery(newQuery)
  684. JiraServiceContextImpl ctx = new JiraServiceContextImpl(userUtil.getUser(sr.ownerUserName))
  685. searchRequestService.validateFilterForUpdate(ctx, sr)
  686. if (ctx.errorCollection.hasAnyErrors()) {
  687. log.error("errors: " + ctx.getErrorCollection())
  688. }
  689. else {
  690. if (! preview) {
  691. searchRequestService.updateFilter(ctx, sr)
  692. }
  693. }
  694. }
  695. else {
  696. log.debug("No need to update name: " + sr.name)
  697. }
  698. }
  699. private def updateAllSearchRequests(String sourceUser, String targetUser) {
  700. EnclosedIterable<SearchRequest> srs = searchRequestManager.getAll()
  701. srs.foreach(new Consumer<SearchRequest>() {
  702. void consume(SearchRequest sr) {
  703. try {
  704. updateSingleRequest(sr, sourceUser, targetUser)
  705. } catch (Exception e) {
  706. log.error("Error checking or updating search request: $sr - this is not fatal but you may wish to check this manually", e)
  707. }
  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. public String getHelpUrl() {
  720. "https://studio.plugins.atlassian.com/wiki/display/GRV/Built-In+Scripts#Built-InScripts-RenameaUserID"
  721. }
  722. }
  723. private class RenamingClauseVisitor implements ClauseVisitor {
  724. Category log = Category.getInstance(RenamingClauseVisitor.class)
  725. SearchHandlerManager searchHandlerManager = ComponentManager.getComponentInstanceOfType(SearchHandlerManager.class) as SearchHandlerManager
  726. ComponentManager componentManager = ComponentManager.getInstance()
  727. CustomFieldManager customFieldManager = componentManager.getCustomFieldManager()
  728. String sourceUser
  729. String targetUser
  730. RenamingClauseVisitor(String sourceUser, String targetUser) {
  731. this.sourceUser = sourceUser
  732. this.targetUser = targetUser
  733. }
  734. Object visit(AndClause andClause) {
  735. Collection<Clause> newClauses = transformSubClauses(andClause)
  736. new AndClause(newClauses)
  737. }
  738. Object visit(NotClause notClause) {
  739. new NotClause(notClause.getSubClause().accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause)
  740. }
  741. Object visit(OrClause orClause) {
  742. Collection<Clause> newClauses = transformSubClauses(orClause)
  743. new OrClause(newClauses)
  744. }
  745. private Collection<Clause> transformSubClauses(Clause mainClause) {
  746. Collection<Clause> newClauses = []
  747. mainClause.getClauses().each {Clause clause ->
  748. newClauses.add(clause.accept(new RenamingClauseVisitor(sourceUser, targetUser)) as Clause)
  749. }
  750. return newClauses
  751. }
  752. Object visit(TerminalClause clause) {
  753. final Collection<ClauseHandler> handlers = searchHandlerManager.getClauseHandler(clause.getName());
  754. Collection<String> fieldIds = searchHandlerManager.getFieldIds(clause.getName())
  755. // If there are multiple handers, eg field has multiple duplicate names, don't deal with it
  756. // ie multiple fields with the same name visible to this user
  757. if (fieldIds?.size() != 1) {
  758. return clause
  759. }
  760. String fieldId = fieldIds.toList().first()
  761. boolean isUserField = false
  762. if (fieldId.startsWith("customfield_")) {
  763. CustomFieldType cfType = customFieldManager.getCustomFieldObject(fieldId).getCustomFieldType()
  764. if (cfType instanceof UserField) {
  765. isUserField = true
  766. }
  767. }
  768. if (["reporter", "assignee"].contains(fieldId) || isUserField) {
  769. Operand operand = clause.getOperand()
  770. Operand newOp = operand.accept(new RenamingOperandVisitor(sourceUser, targetUser)) as Operand
  771. TerminalClauseImpl newTermClause = new TerminalClauseImpl(clause.name, clause.getOperator(), newOp)
  772. return newTermClause
  773. }
  774. clause
  775. }
  776. Object visit(WasClause wasClause) {
  777. // wasClause in 4.3 not handled at the moment
  778. wasClause
  779. }
  780. Object visit(ChangedClause changedClause) {
  781. // changed not handled
  782. return null
  783. }
  784. }
  785. private class RenamingOperandVisitor implements OperandVisitor {
  786. Category log = Category.getInstance(RenamingOperandVisitor.class)
  787. String sourceUser
  788. String targetUser
  789. RenamingOperandVisitor(String sourceUser, String targetUser) {
  790. this.sourceUser = sourceUser
  791. this.targetUser = targetUser
  792. }
  793. Object visit(EmptyOperand empty) {
  794. empty
  795. }
  796. Object visit(FunctionOperand function) {
  797. function
  798. }
  799. Object visit(MultiValueOperand multiValue) {
  800. List<Operand> newOps = multiValue.getValues().collectAll {Operand operand ->
  801. if (operand instanceof SingleValueOperand && operand.getStringValue() == sourceUser) {
  802. return new SingleValueOperand(targetUser)
  803. }
  804. operand
  805. }
  806. new MultiValueOperand(newOps)
  807. }
  808. Object visit(SingleValueOperand singleValueOperand) {
  809. if (singleValueOperand.getStringValue() == sourceUser) {
  810. return new SingleValueOperand(targetUser)
  811. }
  812. singleValueOperand
  813. }
  814. }