| main/src/main/java/com/bloatit/model/Milestone.java |
| 164 | 164 | } |
| 165 | 165 | |
| 166 | 166 | /** |
| 167 | | * Validate the milestone after it has been relreased. |
| 167 | * Validate the milestone after it has been released. |
| 168 | 168 | * |
| 169 | 169 | * @return true, if successful |
| 170 | 170 | * @throws UnauthorizedPublicAccessException |
| ... | ... | |
| 172 | 172 | */ |
| 173 | 173 | public boolean validate() throws UnauthorizedPublicAccessException { |
| 174 | 174 | tryAccess(new RgtMilestone.State(), Action.WRITE); |
| 175 | | return getDao().validate(false); |
| 175 | if (getDao().validate(false)) { |
| 176 | getOffer().notifyMilestoneIsValidated(this); |
| 177 | return true; |
| 178 | } |
| 179 | return false; |
| 176 | 180 | } |
| 177 | 181 | |
| 178 | 182 | /** |
| ... | ... | |
| 187 | 191 | if (!getRights().hasAdminUserPrivilege()) { |
| 188 | 192 | throw new UnauthorizedOperationException(SpecialCode.ADMIN_ONLY); |
| 189 | 193 | } |
| 190 | | return getDao().validate(true); |
| 194 | if (getDao().validate(true)) { |
| 195 | getOffer().notifyMilestoneIsValidated(this); |
| 196 | return true; |
| 197 | } |
| 198 | return false; |
| 191 | 199 | } |
| 192 | 200 | |
| 193 | 201 | /** |
| main/src/main/java/com/bloatit/model/Offer.java |
| 126 | 126 | return isAllValidated; |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | protected void notifyMilestoneIsValidated(final Milestone milestone) { |
| 130 | if (!milestone.getOffer().equals(this)) { |
| 131 | throw new BadProgrammerException("This offer is not the owner of this milestone."); |
| 132 | } |
| 133 | if (getDao().hasMilestonesLeft()) { |
| 134 | getFeatureImplementation().setMilestoneIsValidated(); |
| 135 | } else { |
| 136 | getFeatureImplementation().setOfferIsValidated(); |
| 137 | } |
| 138 | } |
| 139 | |
| 129 | 140 | // Must be internal call. Make me protected ? |
| 130 | 141 | protected boolean shouldValidateCurrentMilestonePart(final Level level) { |
| 131 | 142 | final DaoMilestone currentMilestone = findCurrentDaoMilestone(); |
| ... | ... | |
| 223 | 234 | public Milestone getCurrentMilestone() { |
| 224 | 235 | return Milestone.create(getDao().getCurrentMilestone()); |
| 225 | 236 | } |
| 237 | |
| 238 | // Public data, no right management. |
| 239 | public Milestone getLastMilestone() { |
| 240 | return Milestone.create(getDao().getLastMilestone()); |
| 241 | } |
| 226 | 242 | |
| 227 | 243 | // Public data, no right management. |
| 228 | 244 | public boolean hasRelease() { |
| main/src/main/java/com/bloatit/model/feature/DevelopingState.java |
| 33 | 33 | feature.setFeatureStateUnprotected(getState()); |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | | /* |
| 37 | | * (non-Javadoc) |
| 38 | | * @see |
| 39 | | * com.bloatit.model.feature.AbstractFeatureState#eventMilestoneReleased() |
| 40 | | */ |
| 41 | 36 | @Override |
| 42 | 37 | public AbstractFeatureState eventMilestoneReleased() { |
| 43 | 38 | return this; |
| 44 | 39 | } |
| 45 | 40 | |
| 46 | | /* |
| 47 | | * (non-Javadoc) |
| 48 | | * @see |
| 49 | | * com.bloatit.model.feature.AbstractFeatureState#eventDeveloperCanceled() |
| 50 | | */ |
| 41 | @Override |
| 42 | public AbstractFeatureState eventMilestoneIsValidated() { |
| 43 | return this; |
| 44 | } |
| 45 | |
| 46 | @Override |
| 47 | public AbstractFeatureState eventOfferIsValidated() { |
| 48 | return new FinishedState(feature); |
| 49 | } |
| 50 | |
| 51 | 51 | @Override |
| 52 | 52 | public AbstractFeatureState eventDeveloperCanceled() { |
| 53 | 53 | return new DiscardedState(feature); |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | | /* |
| 57 | | * (non-Javadoc) |
| 58 | | * @see |
| 59 | | * com.bloatit.model.feature.AbstractFeatureState#eventDevelopmentTimeOut() |
| 60 | | */ |
| 61 | 56 | @Override |
| 62 | 57 | public AbstractFeatureState eventDevelopmentTimeOut() { |
| 63 | 58 | // TODO: make Penality. |
| main/src/main/java/com/bloatit/model/feature/FeatureImplementation.java |
| 29 | 29 | import com.bloatit.data.DaoOffer; |
| 30 | 30 | import com.bloatit.data.DaoTeamRight.UserTeamRight; |
| 31 | 31 | import com.bloatit.data.exceptions.NotEnoughMoneyException; |
| 32 | import com.bloatit.framework.exceptions.highlevel.BadProgrammerException; |
| 32 | 33 | import com.bloatit.framework.exceptions.lowlevel.UnauthorizedOperationException; |
| 33 | 34 | import com.bloatit.framework.exceptions.lowlevel.UnauthorizedOperationException.SpecialCode; |
| 34 | 35 | import com.bloatit.framework.exceptions.lowlevel.WrongStateException; |
| ... | ... | |
| 300 | 301 | if (getDao().getSelectedOffer() == null || getDao().getSelectedOffer().getAmount().compareTo(getDao().getContribution()) > 0) { |
| 301 | 302 | throw new WrongStateException("Cannot be in development state, not enough money."); |
| 302 | 303 | } |
| 304 | if (getSelectedOffer().isFinished()) { |
| 305 | throw new BadProgrammerException("Cannot be in development state and have no milestone left."); |
| 306 | } |
| 303 | 307 | getDao().setFeatureState(FeatureState.DEVELOPPING); |
| 304 | 308 | getSelectedOffer().getCurrentMilestone().setDevelopingUnprotected(); |
| 305 | 309 | new TaskDevelopmentTimeOut(getId(), getDao().getSelectedOffer().getCurrentMilestone().getExpirationDate()); |
| main/src/main/java/com/bloatit/web/linkable/admin/AdministrationAction.java |
| 18 | 18 | |
| 19 | 19 | import java.util.List; |
| 20 | 20 | |
| 21 | import com.bloatit.common.Log; |
| 21 | 22 | import com.bloatit.data.DaoFeature.FeatureState; |
| 22 | 23 | import com.bloatit.framework.exceptions.lowlevel.UnauthorizedOperationException; |
| 23 | 24 | import com.bloatit.framework.webprocessor.annotations.ConversionErrorException; |
| ... | ... | |
| 52 | 53 | @ParamConstraint |
| 53 | 54 | @Optional |
| 54 | 55 | private final DisplayableState stateToSet; |
| 56 | |
| 57 | @RequestParam(name = FEATURE_STATE_CODE, role = Role.POST) |
| 58 | @ParamConstraint |
| 59 | @Optional |
| 60 | private final DisplayableFeatureState featureStateToSet; |
| 55 | 61 | |
| 56 | 62 | @SuppressWarnings("unused") |
| 57 | 63 | private final AdministrationActionUrl url; |
| ... | ... | |
| 63 | 69 | contents = url.getContents(); |
| 64 | 70 | action = url.getAction(); |
| 65 | 71 | stateToSet = url.getStateToSet(); |
| 72 | featureStateToSet = url.getFeatureStateToSet(); |
| 66 | 73 | } |
| 67 | 74 | |
| 68 | 75 | @Override |
| ... | ... | |
| 100 | 107 | break; |
| 101 | 108 | case SET_FEATURE_IN_DEVELOPMENT: |
| 102 | 109 | final Feature feature = Loaders.fromStr(Feature.class, id); |
| 103 | | if (feature.getSelectedOffer() == null || feature.getSelectedOffer().getAmount().compareTo(feature.getContribution()) > 0) { |
| 104 | | session.notifyBad("There is no offer or not enough money. So no developement state for id: " + feature.getId() + "."); |
| 105 | | } else { |
| 106 | | feature.setFeatureState(FeatureState.DEVELOPPING); |
| 110 | switch (featureStateToSet) { |
| 111 | case DEVELOPPING: |
| 112 | if (feature.getSelectedOffer() == null || feature.getSelectedOffer().getAmount().compareTo(feature.getContribution()) > 0) { |
| 113 | session.notifyBad("There is no offer or not enough money. So no development state for id: " + feature.getId() + "."); |
| 114 | } else { |
| 115 | feature.setFeatureState(FeatureState.DEVELOPPING); |
| 116 | } |
| 117 | break; |
| 118 | case DISCARDED: |
| 119 | feature.setFeatureState(FeatureState.DISCARDED); |
| 120 | break; |
| 121 | case FINISHED: |
| 122 | feature.setFeatureState(FeatureState.FINISHED); |
| 123 | break; |
| 124 | case PENDING: |
| 125 | case PREPARING: |
| 126 | case NO_FILTER: |
| 127 | default: |
| 128 | Log.web().info("Wrong feature state. Nothing to do."); |
| 129 | session.notifyBad("Wrong feature state. Nothing to do."); |
| 130 | break; |
| 107 | 131 | } |
| 108 | 132 | break; |
| 109 | 133 | case VALIDATE_BATCH: |
| main/src/main/java/com/bloatit/web/linkable/admin/FeatureAdminPage.java |
| 21 | 21 | import java.util.EnumSet; |
| 22 | 22 | |
| 23 | 23 | import com.bloatit.data.DaoFeature; |
| 24 | import com.bloatit.framework.webprocessor.annotations.Optional; |
| 24 | 25 | import com.bloatit.framework.webprocessor.annotations.ParamContainer; |
| 25 | 26 | import com.bloatit.framework.webprocessor.annotations.RequestParam; |
| 26 | 27 | import com.bloatit.framework.webprocessor.components.advanced.HtmlGenericTableModel; |
| ... | ... | |
| 38 | 39 | public final class FeatureAdminPage extends KudosableAdminPage<DaoFeature, Feature, FeatureAdminListFactory> { |
| 39 | 40 | |
| 40 | 41 | @RequestParam(role = RequestParam.Role.POST) |
| 42 | @Optional("NO_FILTER") |
| 41 | 43 | private DisplayableFeatureState filterByState; |
| 42 | 44 | |
| 43 | 45 | @RequestParam(role = RequestParam.Role.POST) |
| 46 | @Optional("NO_FILTER") |
| 44 | 47 | private DisplayableFilterType filterSelectedOffer; |
| 45 | 48 | |
| 46 | 49 | @RequestParam(role = RequestParam.Role.POST) |
| 50 | @Optional("NO_FILTER") |
| 47 | 51 | private DisplayableFilterType filterHasOffer; |
| 48 | 52 | |
| 49 | 53 | @RequestParam(role = RequestParam.Role.POST) |
| 54 | @Optional("NO_FILTER") |
| 50 | 55 | private DisplayableFilterType filterHasContribution; |
| 51 | 56 | |
| 52 | 57 | private final FeatureAdminPageUrl url; |
| main/src/main/java/com/bloatit/web/linkable/admin/MilestoneAdminPage.java |
| 26 | 26 | import com.bloatit.framework.exceptions.lowlevel.UnauthorizedOperationException; |
| 27 | 27 | import com.bloatit.framework.exceptions.lowlevel.UnauthorizedPublicAccessException; |
| 28 | 28 | import com.bloatit.framework.utils.i18n.DateLocale.FormatStyle; |
| 29 | import com.bloatit.framework.webprocessor.annotations.Optional; |
| 29 | 30 | import com.bloatit.framework.webprocessor.annotations.ParamContainer; |
| 30 | 31 | import com.bloatit.framework.webprocessor.annotations.RequestParam; |
| 31 | 32 | import com.bloatit.framework.webprocessor.components.HtmlParagraph; |
| ... | ... | |
| 49 | 50 | public final class MilestoneAdminPage extends IdentifiablesAdminPage<DaoMilestone, Milestone, MilestoneAdminListFactory> { |
| 50 | 51 | |
| 51 | 52 | @RequestParam(role = RequestParam.Role.POST) |
| 53 | @Optional("NOT_SELECTED") |
| 52 | 54 | private final DisplayableMilestoneState milestoneState; |
| 53 | 55 | |
| 54 | 56 | private final MilestoneAdminPageUrl url; |
| main/src/main/java/com/bloatit/web/linkable/bugs/ReportBugPage.java |
| 58 | 58 | private final ReportBugPageUrl url; |
| 59 | 59 | |
| 60 | 60 | public ReportBugPage(final ReportBugPageUrl url) { |
| 61 | | super(url, (url.getOffer() == null ? null : new ReportBugActionUrl(url.getOffer().getCurrentMilestone()))); |
| 61 | super(url, (computeActionUrl(computeMilestone(url)))); |
| 62 | 62 | this.url = url; |
| 63 | | milestone = (url.getOffer() == null ? null:url.getOffer().getCurrentMilestone()); |
| 63 | milestone = computeMilestone(url); |
| 64 | 64 | offer = url.getOffer(); |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | private static Milestone computeMilestone(final ReportBugPageUrl url) { |
| 68 | if (url.getOffer() == null) { |
| 69 | return null; |
| 70 | } |
| 71 | if (url.getOffer().isFinished()) { |
| 72 | return url.getOffer().getLastMilestone(); |
| 73 | } |
| 74 | return url.getOffer().getCurrentMilestone(); |
| 75 | } |
| 76 | |
| 77 | private static ReportBugActionUrl computeActionUrl(final Milestone milestone) { |
| 78 | if (milestone == null) { |
| 79 | return null; |
| 80 | } |
| 81 | return new ReportBugActionUrl(milestone); |
| 82 | } |
| 83 | |
| 67 | 84 | @Override |
| 68 | 85 | protected String createPageTitle() { |
| 69 | 86 | return "Report a bug"; |
| main/src/main/java/com/bloatit/web/linkable/features/FeatureSummaryComponent.java |
| 291 | 291 | final PlaceHolderElement element = new PlaceHolderElement(); |
| 292 | 292 | |
| 293 | 293 | final Offer selectedOffer = feature.getSelectedOffer(); |
| 294 | | final Milestone currentMilestone = selectedOffer.getCurrentMilestone(); |
| 294 | final Milestone currentMilestone = selectedOffer.isFinished() ? selectedOffer.getLastMilestone() : selectedOffer.getCurrentMilestone(); |
| 295 | 295 | if (!selectedOffer.hasRelease()) { |
| 296 | 296 | final Date releaseDate = currentMilestone.getExpirationDate(); |
| 297 | 297 | |
| ... | ... | |
| 422 | 422 | "<0> will receive {0}% of the amount when the {1} remaining FATAL bugs are resolved.", |
| 423 | 423 | fatalSize, |
| 424 | 424 | fatalBugsPercent, |
| 425 | | fatalSize), authorLink))); |
| 425 | fatalSize), |
| 426 | authorLink))); |
| 426 | 427 | } |
| 427 | 428 | if (majorSize > 0 && majorBugsPercent > 0) { |
| 428 | 429 | details.add(new HtmlParagraph(new HtmlMixedText(trn("<0> will receive {0}% of the amount when the remaining MAJOR bug are resolved.", |
| 429 | 430 | "<0> will receive {0}% of the amount when the {1} remaining MAJOR bugs are resolved.", |
| 430 | 431 | majorSize, |
| 431 | 432 | majorBugsPercent, |
| 432 | | majorSize), authorLink))); |
| 433 | majorSize), |
| 434 | authorLink))); |
| 433 | 435 | } |
| 434 | 436 | if (minorSize > 0 && minorBugsPercent > 0) { |
| 435 | 437 | details.add(new HtmlParagraph(new HtmlMixedText(trn("<0> will receive {0}% of the amount when the remaining MINOR bug are resolved.", |
| 436 | 438 | "<0> will receive {0}% of the amount when the {1} remaining MINOR bugs are resolved.", |
| 437 | 439 | minorSize, |
| 438 | 440 | minorBugsPercent, |
| 439 | | minorSize), authorLink))); |
| 441 | minorSize), |
| 442 | authorLink))); |
| 440 | 443 | } |
| 441 | 444 | final HtmlBranch showHideLink = new HtmlSpan().addText(" " + tr("Details")); |
| 442 | 445 | element.add(showHideLink); |
| main/src/test/java/com/bloatit/model/feature/FeatureImplementationTest.java |
| 413 | 413 | return feature; |
| 414 | 414 | } |
| 415 | 415 | |
| 416 | | // public void testFinishedDevelopment() throws NotEnoughMoneyException, |
| 417 | | // UnauthorizedOperationException { |
| 418 | | // final Feature feature = |
| 419 | | // createFeatureAddOffer120AddContribution120BeginDev(); |
| 420 | | // |
| 421 | | // try { |
| 422 | | // feature.getSelectedOffer().; |
| 423 | | // fail(); |
| 424 | | // } catch (final UnauthorizedOperationException e) { |
| 425 | | // assertEquals(UnauthorizedOperationException.SpecialCode.AUTHENTICATION_NEEDED, |
| 426 | | // e.getCode()); |
| 427 | | // } |
| 428 | | // |
| 429 | | // try { |
| 430 | | // feature.authenticate(yoAuthToken); |
| 431 | | // feature.releaseCurrentMilestone(); |
| 432 | | // fail(); |
| 433 | | // } catch (final UnauthorizedOperationException e) { |
| 434 | | // assertEquals(UnauthorizedOperationException.SpecialCode.NON_DEVELOPER_FINISHED_FEATURE, |
| 435 | | // e.getCode()); |
| 436 | | // } |
| 437 | | // |
| 438 | | // feature.authenticate(tomAuthToken); |
| 439 | | // feature.releaseCurrentMilestone(); |
| 440 | | // |
| 441 | | // assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); |
| 442 | | // assertEquals(120, feature.getContribution().intValue()); |
| 443 | | // TODO |
| 444 | | // } |
| 416 | // public void testFinishedDevelopment() throws NotEnoughMoneyException, UnauthorizedOperationException { |
| 417 | // final Feature feature = createFeatureAddOffer120AddContribution120BeginDev(); |
| 418 | // |
| 419 | // try { |
| 420 | // feature.getSelectedOffer(); |
| 421 | // fail(); |
| 422 | // } catch (final UnauthorizedOperationException e) { |
| 423 | // assertEquals(UnauthorizedOperationException.SpecialCode.AUTHENTICATION_NEEDED, e.getCode()); |
| 424 | // } |
| 425 | // |
| 426 | // try { |
| 427 | // feature.authenticate(yoAuthToken); |
| 428 | // feature.releaseCurrentMilestone(); |
| 429 | // fail(); |
| 430 | // } catch (final UnauthorizedOperationException e) { |
| 431 | // assertEquals(UnauthorizedOperationException.SpecialCode.NON_DEVELOPER_FINISHED_FEATURE, e.getCode()); |
| 432 | // } |
| 433 | // |
| 434 | // feature.authenticate(tomAuthToken); |
| 435 | // feature.releaseCurrentMilestone(); |
| 436 | // |
| 437 | // assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); |
| 438 | // assertEquals(120, feature.getContribution().intValue()); |
| 439 | // } |
| 445 | 440 | |
| 446 | 441 | @Test |
| 447 | 442 | public void testOfferWithALotOfMilestone() throws UnauthorizedOperationException, NotEnoughMoneyException { |