[Suiviobsmer-commits] r132 - in trunk/suiviobsmer-ui/src/main: java/fr/ifremer/suiviobsmer/ui/pages webapp
Author: fdesbois Date: 2009-12-17 18:58:07 +0000 (Thu, 17 Dec 2009) New Revision: 132 Modified: trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/SampleRowForm.java trunk/suiviobsmer-ui/src/main/webapp/SampleRowForm.tml Log: Refactor form in proceed... Tapestry problem on refreshing zone with sampleMonths table Modified: trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/SampleRowForm.java =================================================================== --- trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/SampleRowForm.java 2009-12-17 18:57:17 UTC (rev 131) +++ trunk/suiviobsmer-ui/src/main/java/fr/ifremer/suiviobsmer/ui/pages/SampleRowForm.java 2009-12-17 18:58:07 UTC (rev 132) @@ -21,10 +21,7 @@ package fr.ifremer.suiviobsmer.ui.pages; -import org.nuiton.util.PeriodDates; import fr.ifremer.suiviobsmer.SuiviObsmerException; -import fr.ifremer.suiviobsmer.entity.Boat; -import fr.ifremer.suiviobsmer.entity.Company; import fr.ifremer.suiviobsmer.entity.FishingZone; import fr.ifremer.suiviobsmer.entity.Profession; import fr.ifremer.suiviobsmer.entity.ProfessionImpl; @@ -44,16 +41,21 @@ import java.util.Date; import java.util.List; import org.apache.commons.lang.StringUtils; -import org.apache.tapestry5.Block; +import org.apache.tapestry5.ComponentResources; import org.apache.tapestry5.EventContext; +import org.apache.tapestry5.Field; import org.apache.tapestry5.annotations.IncludeStylesheet; import org.apache.tapestry5.annotations.InjectComponent; import org.apache.tapestry5.annotations.Log; import org.apache.tapestry5.annotations.Persist; import org.apache.tapestry5.annotations.Property; -import org.apache.tapestry5.corelib.components.Zone; +import org.apache.tapestry5.beaneditor.BeanModel; +import org.apache.tapestry5.corelib.components.Form; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.services.PropertyAccess; +import org.apache.tapestry5.services.BeanModelSource; +import org.nuiton.util.DateUtils; +import org.nuiton.util.PeriodDates; import org.slf4j.Logger; /** @@ -109,26 +111,32 @@ // load models for select professionSelectModel = null; getProfessionSelectModel(); - programSelectModel = null; - getProgramSelectModel(); +// programSelectModel = null; +// getProgramSelectModel(); fishingZoneSelectModel = null; getFishingZoneSelectModel(); // load main sampleRow (use of sampleRowId in url) sampleRow = null; getSampleRow(); + + if (!isCreateMode()) { + programId = getSampleRow().getProgram().getTopiaId(); + } + + // initialize companyId if exist for companySelect - Company company = getSampleRow().getCompany(); - if (company != null) { - companyId = company.getTopiaId(); - } - immatriculations = getSampleRow().getMainElligibleBoatsAsString(); +// Company company = getSampleRow().getCompany(); +// if (company != null) { +// companyId = company.getTopiaId(); +// } +// immatriculations = getSampleRow().getMainElligibleBoatsAsString(); // Initialize fishingZones, programPeriod and sampleMonths for edition - fishingZones = null; - getFishingZones(); - period = null; - getPeriod(); - sampleMonths = null; - getSampleMonths(); +// fishingZones = null; +// getFishingZones(); +// period = null; +// getPeriod(); +// sampleMonths = null; +// getSampleMonths(); } /*************************** PROFESSION ***********************************/ @@ -136,11 +144,17 @@ @Persist private GenericSelectModel<Profession> professionSelectModel; + @Inject + private BeanModelSource beanModelSource; + + @Inject + private ComponentResources resources; + @Property private String professionId; - @InjectComponent - private Zone professionZone; +// @InjectComponent +// private Zone professionZone; public GenericSelectModel<Profession> getProfessionSelectModel() throws SuiviObsmerException { if (professionSelectModel == null) { @@ -154,25 +168,36 @@ return professionSelectModel; } - void onSelectedFromSearchProfession() throws SuiviObsmerException { - if (log.isDebugEnabled()) { - log.debug("professionId : " + professionId); - } - Profession professionSelected = getProfessionSelectModel().findObject(professionId); - sampleRow.setProfession(professionSelected); + public BeanModel<Profession> getProfessionModel() { + return beanModelSource.createDisplayModel(Profession.class, resources.getMessages()); } - void onSelectedFromAddNewProfession() { - if (log.isDebugEnabled()) { - log.debug("Add new profession"); +// void onSelectedFromSearchProfession() throws SuiviObsmerException { +// if (log.isDebugEnabled()) { +// log.debug("professionId : " + professionId); +// } +// Profession professionSelected = getProfessionSelectModel().findObject(professionId); +// sampleRow.setProfession(professionSelected); +// } + + void onSelectedFromAddNewProfession() throws SuiviObsmerException { + Profession profession = new ProfessionImpl(); + if (!StringUtils.isEmpty(professionId)) { + profession = getProfessionSelectModel().findObject(professionId); + if (log.isDebugEnabled()) { + log.debug("Add new profession from : " + profession); + } + // TODO add this method in Business to copy attributes from the profession in + // argument to a new one + //serviceSampling.getNewProfession(profession); } - sampleRow.setProfession(new ProfessionImpl()); - professionId = null; + getSampleRow().setProfession(profession); + edited = true; } - Block onSuccessFromProfessionForm() throws SuiviObsmerException { - return professionZone.getBody(); - } +// Block onSuccessFromProfessionForm() throws SuiviObsmerException { +// return professionZone.getBody(); +// } /*************************** FISHING ZONE *********************************/ @@ -182,13 +207,13 @@ @Property private String fishingZoneId; - @InjectComponent - private Zone fishingZoneZone; +// @InjectComponent +// private Zone fishingZoneZone; @Property private FishingZone fishingZone; - @Persist + //@Persist private List<FishingZone> fishingZones; @Property @@ -199,16 +224,16 @@ if (log.isInfoEnabled()) { log.info("BUSINESS REQUEST [getFishingZones]"); } - List<FishingZone> fishingZones = serviceReferential.getFishingZones(); - fishingZoneSelectModel = new GenericSelectModel<FishingZone>(fishingZones, FishingZone.class, + List<FishingZone> zones = serviceReferential.getFishingZones(); + fishingZoneSelectModel = new GenericSelectModel<FishingZone>(zones, FishingZone.class, "code", "topiaId", propertyAccess); } return fishingZoneSelectModel; } - public List<FishingZone> getFishingZones() { + public List<FishingZone> getFishingZones() throws SuiviObsmerException { if (fishingZones == null) { - fishingZones = sampleRow.getFishingZone(); + fishingZones = getSampleRow().getFishingZone(); } return fishingZones; } @@ -224,7 +249,7 @@ edited = true; } - void onSelectedFromRemoveFishingZone(int index) { + void onSelectedFromRemoveFishingZone(int index) throws SuiviObsmerException { if (log.isDebugEnabled()) { log.debug("Remove fishingZone at position : " + index); } @@ -232,12 +257,12 @@ edited = true; } - Block onSuccessFromFishingZoneForm() { - if (!edited) { - sampleRow.setFishingZone(fishingZones); - } - return fishingZoneZone.getBody(); - } +// Block onSuccessFromFishingZoneForm() { +// if (!edited) { +// sampleRow.setFishingZone(fishingZones); +// } +// return fishingZoneZone.getBody(); +// } /*************************** PROGRAM & MONTHS *****************************/ @@ -250,23 +275,32 @@ @Property private String programId; - @InjectComponent - private Zone periodZone; +// @InjectComponent +// private Zone periodZone; + /** Program to saved **/ private Program program; @Property private Date month; - @Persist + //@Persist private PeriodDates period; - @Persist + //@Persist private List<SampleMonth> sampleMonths; @Property private SampleMonth sampleMonth; + @InjectComponent + private Field programPeriodBegin; + + @InjectComponent + private Field programPeriodEnd; + + private boolean programChanged; + public GenericSelectModel<Program> getProgramSelectModel() throws SuiviObsmerException { if (programSelectModel == null) { if (log.isInfoEnabled()) { @@ -280,17 +314,23 @@ } public List<Date> getMonths() throws SuiviObsmerException { - return period.getMonths(); + return getPeriod().getMonths(); } + public Program getProgram() throws SuiviObsmerException { + return getSampleRow().getProgram(); + } + public PeriodDates getPeriod() throws SuiviObsmerException { if (period == null) { - program = getSampleRow().getProgram(); - if (program.getPeriodBegin() != null && program.getPeriodEnd() != null) { - period = new PeriodDates(program.getPeriodBegin(), program.getPeriodEnd()); - } else { - period = new PeriodDates(); - } + Date begin = getProgram().getPeriodBegin(); + Date end = getProgram().getPeriodEnd(); + period = new PeriodDates(begin, end); +// if (begin != null && end != null) { +// period = new PeriodDates(begin, end); +// } else { +// period = new PeriodDates(); +// } } return period; } @@ -313,60 +353,97 @@ return new SimpleDateFormat("MM/yyyy"); } + /** + * Search a program from the list. + * The program will be set from the one selected. If no selection, a new one will be set. + * @throws SuiviObsmerException + */ void onSelectedFromSearchProgram() throws SuiviObsmerException { - // Reset programName - programName = null; + program = new ProgramImpl(); + if (!StringUtils.isEmpty(programId)) { + program = getProgramSelectModel().findObject(programId); + } + // Period will be reinitialized from program params + //getSampleRow().setProgram(program); + + + // SampleMonths ?? + + //programName = null; // Reset period to get the one from selected program - period = null; + //period = null; // Reset sampleMonths for new period - sampleMonths = null; - edited = true; + //sampleMonths = null; + //saveProgramAndSampleMonths(); + programChanged = true; } + /** + * Refresh months from program period. + * If the programName is not empty, a new program will be set. + * The period dates will be saved in program (validation will be done in onSuccess). + * @throws SuiviObsmerException + */ void onSelectedFromRefreshMonths() throws SuiviObsmerException { - // Reset sampleMonths cause of period modification - sampleMonths = null; - edited = true; - } - - Block onSuccessFromPeriodForm() throws SuiviObsmerException { - // New program - if (isCreateMode()) { - // Name not empty : suppress programId for select - if (!StringUtils.isEmpty(programName)) { - programId = null; - } - - Program currentProgram = getSampleRow().getProgram(); - // Name changed : init new program with current periodBegin and periodEnd - if (!StringUtils.isEmpty(programName) && !programName.equals(currentProgram.getName())) { - program = new ProgramImpl(); - program.setName(programName); - // ProgramId not empty in select : find program with id in selectModel - } else if (!StringUtils.isEmpty(programId)) { - program = getProgramSelectModel().findObject(programId); - // Nor programId neither programName : reinit program object - } else { - program = new ProgramImpl(); - } - // Set program to session with sampleRow - getSampleRow().setProgram(program); - } - // Save sampleMonth - if (sampleMonths != null) { - getSampleRow().setSampleMonth(sampleMonths); - } - if (!edited) { - // Save period dates + if (!StringUtils.isEmpty(programName)) { + // New program + program = new ProgramImpl(); + program.setName(programName); + //getSampleRow().setProgram(program); + //programId = null; + } else { program = getSampleRow().getProgram(); - program.setPeriodBegin(period.getFromDate()); - program.setPeriodEnd(period.getThruDate()); } - return periodZone.getBody(); + program.setPeriodBegin(period.getFromDate()); + program.setPeriodEnd(period.getThruDate()); + + // SampleMonths ?? + + // Reset sampleMonths cause of period modification + //sampleMonths = null; + //saveProgramAndSampleMonths(); + programChanged = true; } +// private void saveProgramAndSampleMonths() throws SuiviObsmerException { +// // New program +// if (!getSampleRow().hasSampleMonthRealTideTime()) { +// // Name not empty : suppress programId for select +// if (!StringUtils.isEmpty(programName)) { +// programId = null; +// } +// +// Program currentProgram = getSampleRow().getProgram(); +// // Name changed : init new program with current periodBegin and periodEnd +// if (!StringUtils.isEmpty(programName) && !programName.equals(currentProgram.getName())) { +// program = new ProgramImpl(); +// program.setName(programName); +// // ProgramId not empty in select : find program with id in selectModel +// } else if (!StringUtils.isEmpty(programId)) { +// program = getProgramSelectModel().findObject(programId); +// // Nor programId neither programName : reinit program object +// } else { +// program = new ProgramImpl(); +// } +// // Set program to session with sampleRow +// getSampleRow().setProgram(program); +// } +// // Save sampleMonth +// if (sampleMonths != null) { +// getSampleRow().setSampleMonth(sampleMonths); +// } +//// if (!edited) { +//// // Save period dates +//// program = getSampleRow().getProgram(); +//// program.setPeriodBegin(period.getFromDate()); +//// program.setPeriodEnd(period.getThruDate()); +//// } +//// +//// return periodZone.getBody(); +// } + /*************************** SAMPLE ROW ***********************************/ private String sampleRowId; @@ -374,17 +451,20 @@ @Persist private SampleRow sampleRow; - @Property - private String updateComment; + @InjectComponent + private Form sampleRowForm; - @Property - private String immatriculations; +// @Property +// private String updateComment; +// +// @Property +// private String immatriculations; +// +// @Property +// private String companyId; +// +// private GenericSelectModel<Company> companySelectModel; - @Property - private String companyId; - - private GenericSelectModel<Company> companySelectModel; - public SampleRow getSampleRow() throws SuiviObsmerException { if (sampleRow == null) { if (!StringUtils.isEmpty(sampleRowId)) { @@ -395,21 +475,21 @@ } else { sampleRow = serviceSampling.getNewSampleRow(); } - } + } return sampleRow; } - public GenericSelectModel<Company> getCompanySelectModel() throws SuiviObsmerException { - if (companySelectModel == null) { - if (log.isInfoEnabled()) { - log.info("BUSINESS REQUEST [getCompanies]"); - } - List<Company> companies = serviceUser.getCompanies(); - companySelectModel = new GenericSelectModel<Company>(companies, Company.class, - "name", "topiaId", propertyAccess); - } - return companySelectModel; - } +// public GenericSelectModel<Company> getCompanySelectModel() throws SuiviObsmerException { +// if (companySelectModel == null) { +// if (log.isInfoEnabled()) { +// log.info("BUSINESS REQUEST [getCompanies]"); +// } +// List<Company> companies = serviceUser.getCompanies(); +// companySelectModel = new GenericSelectModel<Company>(companies, Company.class, +// "name", "topiaId", propertyAccess); +// } +// return companySelectModel; +// } public boolean isCreateMode() throws SuiviObsmerException { return StringUtils.isEmpty(getSampleRow().getTopiaId()); @@ -421,21 +501,76 @@ @Log Object onSuccessFromSampleRowForm() throws SuiviObsmerException { - if (!edited) { + sampleRowForm.clearErrors(); + if (programChanged) { + String programTopiaId = program.getTopiaId(); + // Reinitialize programId for select if it's not the current one + if (programTopiaId != null && !programTopiaId.equals(programId)) { + programId = null; + // Reinitialize programName, period and sampleMonths if it's the one selected + } else { + programName = null; + period = null; + sampleMonths = null; + } - Company company = getCompanySelectModel().findObject(companyId); - getSampleRow().setCompany(company); + Date periodBegin = program.getPeriodBegin(); + Date periodEnd = program.getPeriodEnd(); - List<Boat> boats = serviceBoat.getBoats(immatriculations); + // Validation for period dates + if (periodBegin == null) { + sampleRowForm.recordError(programPeriodBegin, + "Vous ne pouvez pas rafraîchir les mois avec une date de début de programme vide"); + } + if (periodEnd == null) { + sampleRowForm.recordError(programPeriodEnd, + "Vous ne pouvez pas rafraîchir les mois avec une date de fin de programme vide"); + } - serviceSampling.createUpdateSampleRow(sampleRow, boats); + if (periodBegin != null && periodEnd != null) { - return SamplingPlan.class; + int size = getSampleMonths().size(); - } else { - sampleRow = null; + SampleMonth firstMonth = getSampleMonths().get(0); + SampleMonth lastMonth = getSampleMonths().get(size-1); + + Date firstDate = DateUtils.createDate( + 1, firstMonth.getPeriodMonth(), firstMonth.getPeriodYear()); + + if (periodBegin.after(firstDate) && firstMonth.getRealTidesValue() != 0) { + sampleRowForm.recordError(programPeriodBegin, + "Vous ne pouvez pas réduire la période du programme. " + + "Il existe des enregistrements de marées réels."); + } + } + + if (!sampleRowForm.getHasErrors()) { + // Save program + getSampleRow().setProgram(program); + // Save sampleMonths + //getSampleRow().setSampleMonth(sampleMonths); + } + } else if (!edited) { + //sampleRow.setFishingZone(fishingZones); USEFUL ? + + // Save period dates + //program = getSampleRow().getProgram(); + //program.setPeriodBegin(period.getFromDate()); + //program.setPeriodEnd(period.getThruDate()); + return null; +// Company company = getCompanySelectModel().findObject(companyId); +// getSampleRow().setCompany(company); +// +// List<Boat> boats = serviceBoat.getBoats(immatriculations); +// +// serviceSampling.createUpdateSampleRow(sampleRow, boats); +// +// return SamplingPlan.class; + } else { + // Reinitialize professionId for select to avoid selection from existing Profession + professionId = null; } - return null; + return sampleRowForm; } Modified: trunk/suiviobsmer-ui/src/main/webapp/SampleRowForm.tml =================================================================== --- trunk/suiviobsmer-ui/src/main/webapp/SampleRowForm.tml 2009-12-17 18:57:17 UTC (rev 131) +++ trunk/suiviobsmer-ui/src/main/webapp/SampleRowForm.tml 2009-12-17 18:58:07 UTC (rev 132) @@ -1,6 +1,127 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <t:layout t:pageTitle="Modification d'une ligne du plan" t:contentId="so-samplerow-form" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> + + <t:zone t:id="globalZone"><!--t:update="show"--> + <form t:type="form" t:id="sampleRowForm" t:zone="globalZone"> + + <p> LIGNE DU PLAN : ${sampleRow.code} </p> + <t:errors /> + + <!-- PROFESSION --> + <fieldset> + <legend>Métier</legend> + <t:if t:test="sampleRow.hasSampleMonthRealTideTime()"> + <!-- DISPLAY --> + <t:beandisplay t:id="professionDisplay" t:model="professionModel" t:object="sampleRow.profession" + t:include="codeDCF5, meshSize, size, other, libelle, species" /> + <p:else> + <!-- EDITION --> + <div class="select"> + <t:label t:for="professionSelect" /> : + <input t:type="select" t:id="professionSelect" t:model="professionSelectModel" + t:value="professionId" /> + <input t:type="submit" class="ico add" + t:id="addNewProfession" value="Ajouter un nouveau métier" /> + </div> + <div class="t-beaneditor"> + <t:beaneditor t:id="professionEditor" t:object="sampleRow.profession" + t:include="codeDCF5, meshSize, size, other, libelle, species" /> + </div> + </p:else> + </t:if> + </fieldset> + + <!-- FISHING ZONES --> + <fieldset> + <legend>Zone(s) de pêche</legend> + <t:if t:test="sampleRow.hasSampleMonthRealTideTime()"> + <!-- DISPLAY --> + <ul> + <li t:type="loop" t:source="fishingZones" t:volatile="true" t:value="fishingZone"> + ${fishingZone.code} + </li> + </ul> + <p:else> + <!-- EDITION --> + <div class="select"> + <t:label t:for="fishingZoneSelect" /> : + <input t:type="select" t:id="fishingZoneSelect" t:model="fishingZoneSelectModel" + t:value="fishingZoneId" /> + <input t:type="submit" class="ico add" t:id="addFishingZone" value="Add" + title="Ajouter la zone sélectionnée à la ligne d'échantillonnage" /> + </div> + <div class="clearfix fishingzone-row" t:type="loop" volatile="true" t:source="fishingZones" + t:value="fishingZone" t:index="indexFishingZone"> + <div class="fleft width50"> + <input t:type="submitContext" class="ico remove" t:context="indexFishingZone" + t:id="removeFishingZone" value="Remove" title="Retirer cette zone de la liste liée à la ligne d'échantillonnage"/> + </div> + <div class="fleft"> + ${fishingZone.code} + </div> + </div> + <div class="select"> + <t:label t:for="fishingZonesInfos" /> : + <input t:type="textfield" class="width300" t:id="fishingZonesInfos" value="sampleRow.fishingZonesInfos" /> + </div> + </p:else> + </t:if> + </fieldset> + + <!-- PROGRAM and SAMPLE MONTHS --> + <fieldset> + <legend>Programme et données des marées par mois</legend> + <div class="select"> + <t:if t:test="sampleRow.hasSampleMonthRealTideTime()"> + <!-- DISPLAY PROGRAM --> + Programme : ${sampleRow.program.name} + <p:else> + <!-- EDITION PROGRAM --> + <div class="select"> + <t:label t:for="program"/> : + <input t:type="select" t:id="program" t:model="programSelectModel" value="programId" /> + <input t:type="submit" class="ico search" t:id="searchProgram" value="Search" + title="Afficher les détails du programme"/> + ou <input t:type="textfield" t:id="programName" value="programName" /> + </div> + </p:else> + </t:if> + </div> + <!-- EDITION PROGRAM PERIOD --> + <div class="select"> + <t:label t:for="programPeriodBegin"/> : + du <input t:type="datefield" t:id="programPeriodBegin" value="period.fromDate" /> + jusqu'au <input t:type="datefield" t:id="programPeriodEnd" value="period.thruDate" /> + <input t:type="submit" class="ico refresh" t:id="refreshMonths" value="Refresh" title="Rafraîchir les mois"/> + </div> + <!-- EDITION SAMPLE MONTHS --> + <div class="acenter"> + <table id="so-samplerow-form-months"> + <tr class="firstRow"> + <td t:type="loop" t:source="months" t:value="month" volatile="true"> + <t:output value="month" format="dateFormat" /> + </td> + </tr> + <tr> + <td t:type="loop" class="acenter" volatile="true" t:source="sampleMonths" t:value="sampleMonth"> + <input t:type="textfield" class="width40" t:id="sampleMonth" value="sampleMonth.expectedTidesValue" /> + </td> + </tr> + </table> + </div> + </fieldset> + + <div class="actions"> + <input t:type="submit" t:id="saveData" class="ico save" value="Save" /> + <input t:type="submit" t:id="cancelSave" class="ico undo" value="Undo" /> + </div> + </form> + </t:zone> + + +<!-- + <t:if t:test="createMode"> <div t:type="zone" t:id="professionZone" class="formPart" id="so-samplerow-form-profession"> <form t:type="form" t:id="professionForm" t:zone="so-samplerow-form-profession"> @@ -25,11 +146,11 @@ </form> </div> <p:else> - <!--fieldset class="user-form"> + fieldset class="user-form"> <legend>Métier</legend> <div t:type="beandisplay" t:object="sampleRow.profession" t:include="codeDCF5, meshSize, size, other, libelle, species" /> - </fieldset--> + </fieldset </p:else> </t:if> <t:if t:test="createMode"> @@ -62,7 +183,7 @@ </fieldset> </form> </div> - <!-- TODO else show fishingZone + fishingZoneInfos --> + TODO else show fishingZone + fishingZoneInfos </t:if> <div t:type="zone" t:id="periodZone" class="formPart" id="so-samplerow-form-period"> <form t:type="form" t:id="periodForm" t:zone="so-samplerow-form-period"> @@ -105,7 +226,7 @@ </fieldset> </form> </div> - <!---div t:type="zone" t:id="sampleRowZone" class="formPart" id="so-samplerow-form-other" t:update="show"--> + -div t:type="zone" t:id="sampleRowZone" class="formPart" id="so-samplerow-form-other" t:update="show" <form t:type="form" t:id="sampleRowForm"> <fieldset class="user-form"> <legend>Autres données de la ligne d'échantillonnage</legend> @@ -132,8 +253,8 @@ </div> </fieldset> </form> - <!--/div--> - <!--t:zone t:id="sampleRowZone"> + /div + t:zone t:id="sampleRowZone"> <form t:type="form" t:id="sampleRowForm" t:zone="sampleRowZone"> <p>
participants (1)
-
fdesbois@users.labs.libre-entreprise.org