PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

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