diff --git a/src/main/java/org/olat/course/CourseModule.java b/src/main/java/org/olat/course/CourseModule.java index 843f0261b7d1f591337dad8452bbe32a26c51a1d..0a063e620a5823bdd9f23786a0ba442ed027f2a3 100644 --- a/src/main/java/org/olat/course/CourseModule.java +++ b/src/main/java/org/olat/course/CourseModule.java @@ -68,7 +68,7 @@ public class CourseModule extends AbstractSpringModule { private boolean displayInfoBox; @Value("${course.display.changelog}") private boolean displayChangeLog; - @Value("${course.disclaimer.enabled:false}") + @Value("${course.disclaimer.enabled:true}") private boolean disclaimerEnabled; // Repository types diff --git a/src/main/java/org/olat/course/disclaimer/ui/CourseDisclaimerController.java b/src/main/java/org/olat/course/disclaimer/ui/CourseDisclaimerController.java index d2da3295de8f9f13ca7ac347875bf864ed9d564f..6ec174a69d507f300dd73232fb8097bf3d9c138b 100644 --- a/src/main/java/org/olat/course/disclaimer/ui/CourseDisclaimerController.java +++ b/src/main/java/org/olat/course/disclaimer/ui/CourseDisclaimerController.java @@ -168,13 +168,13 @@ public class CourseDisclaimerController extends FormBasicController { if (disclaimer1Enabled) { allOk &= validateTextInput(disclaimer1TitleEl, 255); - allOk &= validateTextInput(disclaimer1TermsEl, 255); + allOk &= validateTextInput(disclaimer1TermsEl, 2550); allOk &= validateTextInput(disclaimer1Label1El, -1); } if (disclaimer2Enabled) { allOk &= validateTextInput(disclaimer2TitleEl, 255); - allOk &= validateTextInput(disclaimer2TermsEl, 255); + allOk &= validateTextInput(disclaimer2TermsEl, 2550); allOk &= validateTextInput(disclaimer2Label1El, -1); } @@ -370,9 +370,7 @@ public class CourseDisclaimerController extends FormBasicController { courseConfig.setDisclaimerTitle(1, disclaimer1TitleEl.getValue()); courseConfig.setDisclaimerTerms(1, disclaimer1TermsEl.getValue()); courseConfig.setDisclaimerLabel(1, 1, disclaimer1Label1El.getValue()); - if (StringHelper.containsNonWhitespace(disclaimer1Label2El.getValue())) { - courseConfig.setDisclaimerLabel(1, 2, disclaimer1Label2El.getValue()); - } + courseConfig.setDisclaimerLabel(1, 2, disclaimer1Label2El.getValue()); } courseConfig.setDisclaimerEnabled(2, disclaimer2Enabled); @@ -380,9 +378,7 @@ public class CourseDisclaimerController extends FormBasicController { courseConfig.setDisclaimerTitle(2, disclaimer2TitleEl.getValue()); courseConfig.setDisclaimerTerms(2, disclaimer2TermsEl.getValue()); courseConfig.setDisclaimerLabel(2, 1, disclaimer2Label1El.getValue()); - if (StringHelper.containsNonWhitespace(disclaimer2Label2El.getValue())) { - courseConfig.setDisclaimerLabel(2, 2, disclaimer2Label2El.getValue()); - } + courseConfig.setDisclaimerLabel(2, 2, disclaimer2Label2El.getValue()); } CourseFactory.setCourseConfig(courseOres.getResourceableId(), courseConfig); diff --git a/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java b/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java index da7c423c711e5d20e3a73e0af569f5dabbe0aeed..5d570809d1de4a89b278a5e9c4476cb429764243 100644 --- a/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java +++ b/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java @@ -148,7 +148,7 @@ public class PortfolioCourseNode extends AbstractAccessableCourseNode { controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); } else { RepositoryEntry mapEntry = getReferencedRepositoryEntry(); - if(BinderTemplateResource.TYPE_NAME.equals(mapEntry.getOlatResource().getResourceableTypeName())) { + if(mapEntry != null && BinderTemplateResource.TYPE_NAME.equals(mapEntry.getOlatResource().getResourceableTypeName())) { controller = new PortfolioCourseNodeRunController(ureq, wControl, userCourseEnv, this); } else { Translator trans = Util.createPackageTranslator(PortfolioCourseNodeRunController.class, ureq.getLocale()); diff --git a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java index 977bd647f7ef1c01f5a67b07c9e7ff94b96a7702..b324ca1700e773e8bcbc75dd4b5c6b77e48dc4c6 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java +++ b/src/main/java/org/olat/modules/bigbluebutton/manager/BigBlueButtonManagerImpl.java @@ -253,7 +253,7 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ .filter(meeting -> instanceMeetingsIds.contains(meeting.getMeetingId())) .collect(Collectors.toList()); double load = this.calculateLoad(info.getServer(), instanceMeetings); - instanceInfos.add(new BigBlueButtonServerInfos(info.getServer(), instanceMeetings, load)); + instanceInfos.add(new BigBlueButtonServerInfos(info.getServer(), info.isAvailable(), instanceMeetings, load)); } return instanceInfos; } @@ -409,7 +409,7 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ return threads.stream() .filter(MeetingInfosThread::isExecuted) .filter(thread -> !thread.hasErrors()) - .map(thread -> new BigBlueButtonServerInfos(thread.getServer(), thread.getMeetingsInfos(), + .map(thread -> new BigBlueButtonServerInfos(thread.getServer(), thread.isExecuted(), thread.getMeetingsInfos(), calculateLoad(thread.getServer(), thread.getMeetingsInfos()))) .collect(Collectors.toList()); } @@ -600,8 +600,6 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ @Override public String join(BigBlueButtonMeeting meeting, Identity identity, boolean moderator, boolean guest, Boolean isRunning, BigBlueButtonErrors errors) { - this.getAvailableServer(); - String joinUrl = null; if(isRunning != null && isRunning.booleanValue() && meeting.getServer() != null) { joinUrl = buildJoinUrl(meeting, meeting.getServer(), identity, moderator, guest); @@ -687,7 +685,7 @@ public class BigBlueButtonManagerImpl implements BigBlueButtonManager, Initializ private boolean createBigBlueButtonMeeting(BigBlueButtonMeeting meeting, BigBlueButtonErrors errors) { BigBlueButtonMeetingTemplate template = meeting.getTemplate(); BigBlueButtonServer server = meeting.getServer(); - if(!server.isEnabled()) { + if(server == null || !server.isEnabled()) { errors.append(new BigBlueButtonError(BigBlueButtonErrorCodes.serverDisabled)); return false; } diff --git a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java index d0162ffd38589ef5e4bf75c873735584c8880840..57535d23bd7b38ee8d28319a10a55ec4cec5e47d 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java +++ b/src/main/java/org/olat/modules/bigbluebutton/model/BigBlueButtonServerInfos.java @@ -19,6 +19,7 @@ */ package org.olat.modules.bigbluebutton.model; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -33,19 +34,29 @@ import org.olat.modules.bigbluebutton.BigBlueButtonServer; public class BigBlueButtonServerInfos { private double load; + private boolean available; private final BigBlueButtonServer server; private final List<BigBlueButtonMeetingInfos> meetingsInfos; - public BigBlueButtonServerInfos(BigBlueButtonServer server, List<BigBlueButtonMeetingInfos> meetingsInfos, double load) { + public BigBlueButtonServerInfos(BigBlueButtonServer server, boolean available, List<BigBlueButtonMeetingInfos> meetingsInfos, double load) { this.load = load; this.server = server; + this.available = available; this.meetingsInfos = meetingsInfos; } + public static final BigBlueButtonServerInfos empty(BigBlueButtonServer server) { + return new BigBlueButtonServerInfos(server, false, new ArrayList<>(), 0.0d); + } + public double getLoad() { return load; } + public boolean isAvailable() { + return available; + } + public BigBlueButtonServer getServer() { return server; } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java index 04baceada2c51bc7f502d72fec6cceee780fc13f..4cdb7f704fb761a4261199034f57914ee532385a 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersController.java @@ -65,7 +65,7 @@ public class BigBlueButtonAdminServersController extends FormBasicController { protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.enabled)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.status, new StatusCellRenderer(getTranslator()))); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.url)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.capacityFactor, new CapacityFactorCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ServersCols.moderatorCount)); diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java index dbe5e31e462db3b3cd09cdc027f2157ab0c36d51..f1c2ea7688aff7aa6916b72bd68d7dd56cb3aa17 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonAdminServersTableModel.java @@ -73,7 +73,7 @@ implements SortableFlexiTableDataModel<BigBlueButtonServerRow>, FilterableFlexiT public Object getValueAt(BigBlueButtonServerRow row, int col) { switch(COLS[col]) { case url: return row.getUrl(); - case enabled: return row.isEnabled(); + case status: return row.isEnabled(); case capacityFactor: return row.getCapacityFactor(); case moderatorCount: return allInstances ? row.getAllInstancesServerInfos().getModeratorCount() : row.getServerInfos().getModeratorCount(); @@ -105,7 +105,7 @@ implements SortableFlexiTableDataModel<BigBlueButtonServerRow>, FilterableFlexiT public enum ServersCols implements FlexiSortableColumnDef { url("table.header.server.url"), - enabled("table.header.server.enabled"), + status("table.header.server.enabled"), capacityFactor("table.header.capacity.factor"), moderatorCount("table.header.moderator.count"), participantCount("table.header.participant.count"), diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java index 62506b318c3998700c2b2f208d990b4dd7fdd6a1..036e0bd9e7aa9d1d3f6caa83d65bc21d7a8ae547 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonServerRow.java @@ -19,8 +19,6 @@ */ package org.olat.modules.bigbluebutton.ui; -import java.util.ArrayList; - import org.olat.modules.bigbluebutton.BigBlueButtonServer; import org.olat.modules.bigbluebutton.model.BigBlueButtonServerInfos; @@ -40,10 +38,8 @@ public class BigBlueButtonServerRow { BigBlueButtonServerInfos allInstanceServerInfos, BigBlueButtonServerInfos serverInfos) { this.server = server; - this.serverInfos = serverInfos == null - ? new BigBlueButtonServerInfos(server, new ArrayList<>(), 0.0d) : serverInfos; - this.allInstanceServerInfos = allInstanceServerInfos == null - ? new BigBlueButtonServerInfos(server, new ArrayList<>(), 0.0d) : allInstanceServerInfos; + this.serverInfos = serverInfos == null ? BigBlueButtonServerInfos.empty(server) : serverInfos; + this.allInstanceServerInfos = allInstanceServerInfos == null ? BigBlueButtonServerInfos.empty(server) : allInstanceServerInfos; } public String getUrl() { @@ -54,6 +50,10 @@ public class BigBlueButtonServerRow { return server.isEnabled(); } + public boolean isAvailable() { + return allInstanceServerInfos.isAvailable(); + } + public Double getCapacityFactor() { return server.getCapacityFactory(); } diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/StatusCellRenderer.java b/src/main/java/org/olat/modules/bigbluebutton/ui/StatusCellRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..77bcaef32818a690dedee3d03d07324aad2255b0 --- /dev/null +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/StatusCellRenderer.java @@ -0,0 +1,66 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.bigbluebutton.ui; + +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.URLBuilder; +import org.olat.core.gui.translator.Translator; + +/** + * + * Initial date: 8 avr. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class StatusCellRenderer implements FlexiCellRenderer { + + private final Translator translator; + + public StatusCellRenderer(Translator translator) { + this.translator = translator; + } + + @Override + public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source, + URLBuilder ubu, Translator trans) { + Object obj = source.getFlexiTableElement().getTableDataModel().getObject(row); + if(obj instanceof BigBlueButtonServerRow) { + BigBlueButtonServerRow server = (BigBlueButtonServerRow)obj; + + String title; + String statusCssClass; + if(!server.isEnabled()) { + title = "server.status.disabled"; + statusCssClass = "o_icon_disabled"; + } else if(!server.isAvailable()) { + statusCssClass = "o_icon_warn"; + title = "server.status.offline"; + } else { + statusCssClass = "o_icon_accept"; + title = "server.status.available"; + } + target.append("<span title=\"").append(translator.translate(title)) + .append("\"><i class='o_icon ").append(statusCssClass).append("'> </i></span>"); + } + } +} diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties index 003e7c980fdb9ac01fe800054ff1daf8249f2106..44b254b9d6b085b48201f0dc405cbc8fff4a1257 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties @@ -50,7 +50,7 @@ error.too.long.time=Zeit ist zu lang. Es sind maximal {0} Minuten erlaubt. error.url.invalid=Ung\u00FCltige Serveradresse filter.all.instances=Alle OpenOlats filter.this.instance=Dieses OpenOlat -meeting.create.intro=Der Online-Termin wurde vom Betreuer noch nicht er\u00F6ffnet. Teilnehmer k\u00F6nnen den Raum f\u00FCr noch nicht betreten. +meeting.create.intro=Der Online-Termin wurde vom Betreuer noch nicht er\u00F6ffnet. Teilnehmer k\u00F6nnen den Raum noch nicht betreten. meeting.day=Datum des Meetings meeting.deleted=Das Meeting wurde erfolgreich gel\u00F6scht. meeting.description=Beschreibung @@ -63,7 +63,7 @@ meeting.leadTime=Vorlaufzeit (Min.) meeting.name=Name meeting.permanent=Online-Termin planung meeting.permanent.on=Ohne Datum -meeting.recurring.end=End wiederkehrendes Datum +meeting.recurring.end=Ende wiederkehrendes Datum meeting.recurring.start=Start wiederkehrendes Datum meeting.resource=Kontext meeting.start=Beginn @@ -98,6 +98,9 @@ role.group=Gruppenmitglied role.owner=Kursbesitzer server.overloaded=F\u00FCr das gew\u00E4hlten Datum/Uhrzeit ist kein Raum verf\u00FCgbar. W\u00E4hlen Sie ein anderes Datum/Uhrzeit oder eine andere Raumvorlage. servers.title=Server +server.status.available=Verf\u00FCgbar +server.status.offline=Scheint offline zu sein +server.status.disabled=Abgeschaltet table.header.available=Verf\u00FCgbarkeit table.header.breakout.meetings=\# Breakout table.header.breakout.recording.meetings=\# Breakout Recording @@ -121,6 +124,7 @@ table.header.recording.open=\u00D6ffnen table.header.recording.start=Beginn table.header.recording.type=Typ table.header.server.enabled=Eingeschaltet +table.header.server.status=Status table.header.server.recording=Aufzeichnungen URL table.header.server.url=URL table.header.system=System diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties index c2a39241c04093ff380107a1f98f586727dafa39..94b9c8756aacd75d6864816796cf58cc5b36597d 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_en.properties @@ -97,6 +97,9 @@ role.coach=Coach role.group=Group user role.owner=Course owner server.overloaded=There is no room available for the choosen date/time. Choose another date/time or another room template. +server.status.available=Available +server.status.offline=Seems to be offline +server.status.disabled=Disabled servers.title=Servers table.header.available=Availability table.header.breakout.meetings=\# Breakout @@ -121,6 +124,7 @@ table.header.recording.open=Open table.header.recording.start=Start table.header.recording.type=Type table.header.server.enabled=Enabled +table.header.server.status=Status table.header.server.recording=Recording URL table.header.server.url=URL table.header.system=System diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/recurring/BigBlugButtonMeetingConfigurationController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/recurring/BigBlugButtonMeetingConfigurationController.java index 7f256f8d822926a920e06275399d13aa03b42aca..20725034bd278063f1a4bf303fa399b7a02e4d79 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/recurring/BigBlugButtonMeetingConfigurationController.java +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/recurring/BigBlugButtonMeetingConfigurationController.java @@ -192,6 +192,12 @@ public class BigBlugButtonMeetingConfigurationController extends StepFormBasicCo endRecurringDateEl.setErrorKey("form.legende.mandatory", null); allOk &= false; } + + if(startRecurringDateEl.getDate() != null && endRecurringDateEl.getDate() != null + && endRecurringDateEl.getDate().before(startRecurringDateEl.getDate())) { + endRecurringDateEl.setErrorKey("error.start.after.end", null); + allOk &= false; + } startTimeEl.clearError(); if(startTimeEl.getDate() == null) { diff --git a/src/main/java/org/olat/modules/grading/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/grading/ui/_i18n/LocalStrings_de.properties index b6dd4277ee2e805a877392d7494bdabce09381d3..5b353300be0122b69c08d560ffa2dad752bf0d43 100644 --- a/src/main/java/org/olat/modules/grading/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/grading/ui/_i18n/LocalStrings_de.properties @@ -31,7 +31,7 @@ configuration.informations.resource.open=\u00D6ffnen configuration.informations.resource.owners=Besitzer configuration.notification.afterTestSubmission=Sofort nach Testabschluss configuration.notification.body=Benachrichtigung -configuration.notification.onceDay=Einmal pro Tag um Mitternacht +configuration.notification.onceDay=Einmal pro Tag nachts configuration.notification.subject=Benachrichtigung Betreffzeile configuration.notification.type=Benachrichtigung bei neuen Zuweisungen configuration.second.reminder.body=Erinnerung diff --git a/src/main/java/org/olat/modules/portfolio/ui/CourseTemplateSearchController.java b/src/main/java/org/olat/modules/portfolio/ui/CourseTemplateSearchController.java index 78cee001b5a22a5ddfe6cce1d4a176018a237558..938ba5daec8a035d97eed7cff5c7860d154927b9 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/CourseTemplateSearchController.java +++ b/src/main/java/org/olat/modules/portfolio/ui/CourseTemplateSearchController.java @@ -137,7 +137,7 @@ public class CourseTemplateSearchController extends FormBasicController { .getNodeById(pNode.getIdent()); if (treeNode != null && treeNode.isAccessible()) { RepositoryEntry refEntry = pNode.getReferencedRepositoryEntry(); - if("BinderTemplate".equals(refEntry.getOlatResource().getResourceableTypeName())) { + if(refEntry != null && "BinderTemplate".equals(refEntry.getOlatResource().getResourceableTypeName())) { RepositoryEntry courseEntry = uce.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); CurrentBinder binderKey = new CurrentBinder(courseEntry.getKey(), pNode.getIdent()); diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index aa7af4cb5c76496652ee85e2dc439d402e210074..c44b2e364250f7c0a5885f4ee6985e350a711c1c 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -1261,8 +1261,8 @@ ldap.learningResourceManagerRoleValue= # Build properties ##### application.name=OpenOlat -build.version=15.pre.7 -build.identifier=openolat150pre7-dev +build.version=15.pre.8 +build.identifier=openolat150pre8-dev build.repo.revision=local-devel #####