PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/aschuma/scriptrunner-public-2.0.9
Groovy | 925 lines | 764 code | 114 blank | 47 comment | 126 complexity | 466ae049102aa7bb8cf3cf6ab2f7ba9c 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. 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. // 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. if (doMerge && targetGvs.find {
  470. gv.getFields(matchFields) == it.getFields(matchFields)
  471. }) {
  472. log.debug("Remove role record")
  473. gv.remove()
  474. }
  475. else {
  476. gv.set("roletypeparameter", targetUser)
  477. gv.store()
  478. }
  479. }
  480. cachingProjectRoleAndActorStore.clearCaches()
  481. }
  482. // SharePermissions not required as cannot share with a user
  483. // worklogs
  484. gvs = gd.findByOr("Worklog", [author: sourceUser, updateauthor: sourceUser])
  485. msg = "Update ${gvs.size()} Worklog records"
  486. log.debug(msg)
  487. msgs << msg
  488. if (!preview) {
  489. gvs.each {GenericValue gv ->
  490. if (gv.get("author") == sourceUser) {
  491. gv.set("author", targetUser)
  492. }
  493. if (gv.get("updateauthor") == sourceUser) {
  494. gv.set("updateauthor", targetUser)
  495. }
  496. gv.store()
  497. }
  498. }
  499. // project and comp leads
  500. gvs = gd.findByAnd("Project", [lead: sourceUser])
  501. msg = "Update ${gvs.size()} Project records"
  502. log.debug(msg)
  503. msgs << msg
  504. if (!preview) {
  505. gvs.each {GenericValue gv ->
  506. gv.set("lead", targetUser)
  507. gv.store()
  508. }
  509. }
  510. gvs = gd.findByAnd("Component", [lead: sourceUser])
  511. msg = "Update ${gvs.size()} Component records"
  512. log.debug(msg)
  513. msgs << msg
  514. if (!preview) {
  515. gvs.each {GenericValue gv ->
  516. gv.set("lead", targetUser)
  517. gv.store()
  518. }
  519. }
  520. // notification schemes
  521. gvs = gd.findByAnd("Notification", [type: SingleUser.DESC, parameter: sourceUser])
  522. targetGvs = gd.findByAnd("Notification", [type: SingleUser.DESC, parameter: targetUser])
  523. msg = "Update ${gvs.size()} Notification records"
  524. log.debug(msg)
  525. msgs << msg
  526. if (!preview) {
  527. gvs.each {GenericValue gv ->
  528. if (doMerge && targetGvs.find {
  529. gv.getFields(["scheme", "eventTypeId", "type"]) == it.getFields(["scheme", "eventTypeId", "type"])
  530. }) {
  531. log.debug("Remove Notification record")
  532. gv.remove()
  533. }
  534. else {
  535. gv.set("parameter", targetUser)
  536. gv.store()
  537. }
  538. }
  539. notificationSchemeManager.flushProjectSchemes()
  540. }
  541. // permission schemes
  542. gvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: sourceUser])
  543. targetGvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: targetUser])
  544. msg = "Update ${gvs.size()} SchemePermissions records"
  545. log.debug(msg)
  546. msgs << msg
  547. if (!preview) {
  548. List matchFields = ['scheme', 'permission', 'type', 'parameter']
  549. gvs.each {GenericValue gv ->
  550. if (doMerge && targetGvs.find {
  551. gv.getFields(matchFields) == it.getFields(matchFields)
  552. }) {
  553. log.debug("Remove Notification record")
  554. gv.remove()
  555. }
  556. else {
  557. gv.set("parameter", targetUser)
  558. gv.store()
  559. }
  560. }
  561. defaultPermissionSchemeManager.flushProjectSchemes()
  562. defaultPermissionSchemeManager.flushSchemeEntities()
  563. }
  564. // issue security schemes
  565. gvs = gd.findByAnd("SchemeIssueSecurities", [type: "user", parameter: sourceUser])
  566. targetGvs = gd.findByAnd("SchemePermissions", [type: "user", parameter: targetUser])
  567. msg = "Update ${gvs.size()} SchemeIssueSecurities records"
  568. log.debug(msg)
  569. msgs << msg
  570. if (!preview) {
  571. List matchFields = ['scheme', 'security', 'type', 'parameter']
  572. gvs.each {GenericValue gv ->
  573. if (doMerge && targetGvs.find {
  574. gv.getFields(matchFields) == it.getFields(matchFields)
  575. }) {
  576. log.debug("Remove Notification record")
  577. gv.remove()
  578. }
  579. else {
  580. gv.set("parameter", targetUser)
  581. gv.store()
  582. }
  583. }
  584. securitySchemeManager.flushProjectSchemes()
  585. }
  586. // user and multi-user custom fields
  587. gvs = gd.findByCondition("CustomField",
  588. new EntityExpr("customfieldtypekey", EntityOperator.IN,
  589. [
  590. "com.atlassian.jira.plugin.system.customfieldtypes:userpicker",
  591. "com.atlassian.jira.plugin.system.customfieldtypes:multiuserpicker"
  592. ])
  593. ,
  594. ["id"], Collections.<String> emptyList())
  595. Long cfValueKount = 0
  596. gvs.each {GenericValue gv ->
  597. gv.getRelatedByAnd("ChildCustomFieldValue", [stringvalue: sourceUser]).each {GenericValue cfGv ->
  598. cfValueKount++
  599. if (!preview) {
  600. cfGv.set("stringvalue", targetUser)
  601. cfGv.store()
  602. }
  603. reindexIssueIds.add(cfGv.getLong("issue"))
  604. }
  605. }
  606. msg = "Update $cfValueKount CustomFieldValue records"
  607. log.debug(msg)
  608. msgs << msg
  609. gvs = gd.findByAnd("OSHistoryStep", [caller: sourceUser])
  610. msg = "Update ${gvs.size()} OSHistoryStep records"
  611. log.debug(msg)
  612. msgs << msg
  613. if (!preview) {
  614. gvs.each {GenericValue gv ->
  615. gv.set("caller", targetUser)
  616. gv.store()
  617. }
  618. }
  619. // fileattachments
  620. gvs = gd.findByAnd("FileAttachment", [author: sourceUser])
  621. msg = "Update ${gvs.size()} FileAttachment records"
  622. log.debug(msg)
  623. msgs << msg
  624. if (!preview) {
  625. gvs.each {GenericValue gv ->
  626. gv.set("author", targetUser)
  627. gv.store()
  628. }
  629. }
  630. // -------------------------------------- Finalise --------------------------------------
  631. if (! preview) {
  632. log.debug("Commit transaction")
  633. CoreTransactionUtil.commit(began);
  634. // Only for 4.1 and above - clearing the other caches are not necessary above but kept there if
  635. // I decide to backport this
  636. log.debug("Publish clear cache event")
  637. ComponentManager.getComponentInstanceOfType(EventPublisher.class).publish(ClearCacheEvent.INSTANCE);
  638. // reindex issues
  639. reindexIssueIds.each {Long issueId ->
  640. reindexIssue(issueManager, issueId, log, indexManager);
  641. }
  642. }
  643. msg = "Reindex ${reindexIssueIds.size()} issues"
  644. log.debug(msg)
  645. msgs << msg
  646. }
  647. catch (Exception e) {
  648. log.error("Problem renaming user, rolling back")
  649. CoreTransactionUtil.rollback(began)
  650. throw e
  651. }
  652. finally {
  653. // not sure if we need anything here
  654. }
  655. }
  656. private List<EntityExpr> getUserAssociationConditionList(String userId) {
  657. List userAssocCList = [
  658. new EntityExpr("sourceName", EntityOperator.EQUALS, userId),
  659. new EntityExpr("associationType", EntityOperator.IN,
  660. [UserAssociationParser.ASSOCIATION_TYPE_WATCH_ISSUE, UserAssociationParser.ASSOCIATION_TYPE_VOTE_ISSUE]),
  661. ]
  662. return userAssocCList
  663. }
  664. def updateSingleRequest(SearchRequest sr, String sourceUser, String targetUser) {
  665. Query query = sr.getQuery()
  666. Query newQuery
  667. // A query may have no where clause and select everything... if so .accept dies
  668. if (query.getWhereClause()) {
  669. newQuery = transformSearchRequest(query, sourceUser, targetUser)
  670. }
  671. else {
  672. newQuery = query
  673. }
  674. if (query.whereClause.toString() != newQuery.whereClause.toString()) {
  675. // this is stupid but setting a queryString seems to blow away the whereClause, so we do this twice
  676. nSearchReqsUpdated++
  677. log.debug("Update sr name: " + sr.name)
  678. // log.debug("queryString: " + newQuery.queryString)
  679. // log.debug("newQuery: $newQuery")
  680. sr.setQuery(newQuery)
  681. JiraServiceContextImpl ctx = new JiraServiceContextImpl(userUtil.getUser(sr.ownerUserName))
  682. searchRequestService.validateFilterForUpdate(ctx, sr)
  683. if (ctx.errorCollection.hasAnyErrors()) {
  684. log.error("errors: " + ctx.getErrorCollection())
  685. }
  686. else {
  687. if (! preview) {
  688. searchRequestService.updateFilter(ctx, sr)
  689. }
  690. }
  691. }
  692. else {
  693. log.debug("No need to update name: " + sr.name)
  694. }
  695. }
  696. private def updateAllSearchRequests(String sourceUser, String targetUser) {
  697. EnclosedIterable<SearchRequest> srs = searchRequestManager.getAll()
  698. srs.foreach(new Consumer<SearchRequest>() {
  699. void consume(SearchRequest sr) {
  700. try {
  701. updateSingleRequest(sr, sourceUser, targetUser)
  702. } catch (Exception e) {
  703. log.error("Error checking or updating search request: $sr - this is not fatal but you may wish to check this manually", e)
  704. }
  705. }
  706. })
  707. msg = "Update parameters for $nSearchReqsUpdated search requests"
  708. log.debug(msg)
  709. msgs << msg
  710. }
  711. String getDescription(Map params, boolean forPreview) {
  712. preview = true
  713. Map out = doScript(params)
  714. out["output"]
  715. }
  716. public String getHelpUrl() {
  717. "https://studio.plugins.atlassian.com/wiki/display/GRV/Built-In+Scripts#Built-InScripts-RenameaUserID"
  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. Object visit(WasClause wasClause) {
  774. // wasClause in 4.3 not handled at the moment
  775. wasClause
  776. }
  777. Object visit(ChangedClause changedClause) {
  778. // changed not handled
  779. return null
  780. }
  781. }
  782. private class RenamingOperandVisitor implements OperandVisitor {
  783. Category log = Category.getInstance(RenamingOperandVisitor.class)
  784. String sourceUser
  785. String targetUser
  786. RenamingOperandVisitor(String sourceUser, String targetUser) {
  787. this.sourceUser = sourceUser
  788. this.targetUser = targetUser
  789. }
  790. Object visit(EmptyOperand empty) {
  791. empty
  792. }
  793. Object visit(FunctionOperand function) {
  794. function
  795. }
  796. Object visit(MultiValueOperand multiValue) {
  797. List<Operand> newOps = multiValue.getValues().collectAll {Operand operand ->
  798. if (operand instanceof SingleValueOperand && operand.getStringValue() == sourceUser) {
  799. return new SingleValueOperand(targetUser)
  800. }
  801. operand
  802. }
  803. new MultiValueOperand(newOps)
  804. }
  805. Object visit(SingleValueOperand singleValueOperand) {
  806. if (singleValueOperand.getStringValue() == sourceUser) {
  807. return new SingleValueOperand(targetUser)
  808. }
  809. singleValueOperand
  810. }
  811. }