Skip to content
Snippets Groups Projects
Commit eca61484 authored by srosse's avatar srosse
Browse files

OO-291: implement the popup to manage the areas in a course, add first draft...

OO-291: implement the popup to manage the areas in a course, add first draft of the rights management
parent 008c8b30
No related branches found
No related tags found
No related merge requests found
Showing
with 537 additions and 546 deletions
......@@ -23,13 +23,12 @@
* under the Apache 2.0 license as the original file.
*/
package org.olat.group.ui.area;
package org.olat.course.area;
import java.util.List;
import org.apache.commons.lang.StringEscapeUtils;
import org.olat.core.gui.components.table.DefaultTableDataModel;
import org.olat.core.gui.translator.Translator;
import org.olat.core.util.Formatter;
import org.olat.core.util.filter.FilterFactory;
import org.olat.group.area.BGArea;
......@@ -43,16 +42,12 @@ import org.olat.group.area.BGArea;
public class BGAreaTableModel extends DefaultTableDataModel<BGArea> {
private static final int COLUMN_COUNT = 3;
// package-local to avoid synthetic accessor method.
Translator translator;
/**
* @param owned list of group areas
* @param translator
*/
public BGAreaTableModel(List<BGArea> owned, Translator translator) {
public BGAreaTableModel(List<BGArea> owned) {
super(owned);
this.translator = translator;
}
/**
......
/**
* <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.course.area;
import java.util.Collections;
import java.util.List;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.panel.Panel;
import org.olat.core.gui.components.table.DefaultColumnDescriptor;
import org.olat.core.gui.components.table.StaticColumnDescriptor;
import org.olat.core.gui.components.table.Table;
import org.olat.core.gui.components.table.TableController;
import org.olat.core.gui.components.table.TableEvent;
import org.olat.core.gui.components.table.TableGuiConfiguration;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.gui.control.generic.modal.DialogBoxController;
import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
import org.olat.core.gui.translator.Translator;
import org.olat.core.util.Util;
import org.olat.group.area.BGArea;
import org.olat.group.area.BGAreaManager;
import org.olat.group.ui.NewAreaController;
import org.olat.group.ui.area.BGAreaEditController;
import org.olat.repository.RepositoryTableModel;
import org.olat.resource.OLATResource;
/**
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class CourseAreasController extends BasicController {
private static final String TABLE_ACTION_EDIT = "tbl_edit";
private static final String TABLE_ACTION_DELETE = "tbl_del";
private final Panel mainPanel;
private final Link createAreaLink;
private final VelocityContainer mainVC;
private final TableController tableCtrl;
private DialogBoxController deleteDialogCtr;
private BGAreaEditController editController;
private NewAreaController newAreaController;
private final BGAreaManager areaManager;
private final BGAreaTableModel areaDataModel;
private final OLATResource resource;
public CourseAreasController(UserRequest ureq, WindowControl wControl, OLATResource resource) {
super(ureq, wControl);
this.resource = resource;
areaManager = CoreSpringFactory.getImpl(BGAreaManager.class);
Translator resourceTrans = Util.createPackageTranslator(RepositoryTableModel.class, getLocale(), getTranslator());
TableGuiConfiguration tableConfig = new TableGuiConfiguration();
tableConfig.setTableEmptyMessage(translate("resources.noresources"));
tableCtrl = new TableController(tableConfig, ureq, getWindowControl(), resourceTrans);
listenTo(tableCtrl);
tableCtrl.addColumnDescriptor(new DefaultColumnDescriptor("table.header.name", 0, null, getLocale()));
tableCtrl.addColumnDescriptor(new DefaultColumnDescriptor("table.header.description", 1, null, getLocale()));
tableCtrl.addColumnDescriptor(new StaticColumnDescriptor(TABLE_ACTION_EDIT, "action", translate("edit")));
tableCtrl.addColumnDescriptor(new StaticColumnDescriptor(TABLE_ACTION_DELETE, "action", translate("delete")));
areaDataModel = new BGAreaTableModel(Collections.<BGArea>emptyList());
tableCtrl.setTableDataModel(areaDataModel);
loadModel();
mainVC = createVelocityContainer("area_list");
mainVC.put("areaList", tableCtrl.getInitialComponent());
createAreaLink = LinkFactory.createButton("create.area", mainVC, this);
mainVC.put("createArea", createAreaLink);
mainPanel = putInitialPanel(mainVC);
}
@Override
protected void doDispose() {
//
}
private void loadModel() {
List<BGArea> areas = areaManager.findBGAreasInContext(resource);
areaDataModel.setObjects(areas);
tableCtrl.modelChanged();
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
if(source == createAreaLink) {
removeAsListenerAndDispose(newAreaController);
newAreaController = new NewAreaController(ureq, getWindowControl(), resource, false, null);
listenTo(newAreaController);
mainPanel.pushContent(newAreaController.getInitialComponent());
}
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(tableCtrl == source) {
if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) {
TableEvent te = (TableEvent) event;
String actionid = te.getActionId();
if(TABLE_ACTION_EDIT.equals(actionid)) {
BGArea area = areaDataModel.getObject(te.getRowId());
doEdit(ureq, area);
} else if (TABLE_ACTION_DELETE.equals(actionid)) {
BGArea area = areaDataModel.getObject(te.getRowId());
String text = translate("delete.area.description", new String[]{ area.getName() });
deleteDialogCtr = activateYesNoDialog(ureq, translate("delete.area.title"), text, deleteDialogCtr);
deleteDialogCtr.setUserObject(area);
}
}
} else if (source == deleteDialogCtr) {
if (DialogBoxUIFactory.isYesEvent(event)) { // yes case
BGArea area = (BGArea)deleteDialogCtr.getUserObject();
doDelete(area);
}
} else if (source == newAreaController) {
if(event == Event.CANCELLED_EVENT ) {
mainPanel.popContent();
} else if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
BGArea area = newAreaController.getCreatedArea();
loadModel();
mainPanel.popContent();
doEdit(ureq, area);
removeAsListenerAndDispose(newAreaController);
newAreaController = null;
}
} else if (source == editController) {
if(event == Event.BACK_EVENT) {
mainPanel.popContent();
removeAsListenerAndDispose(editController);
editController = null;
}
}
super.event(ureq, source, event);
}
private void doDelete(BGArea area) {
areaManager.deleteBGArea(area);
loadModel();
}
private void doEdit(UserRequest ureq, BGArea area) {
removeAsListenerAndDispose(editController);
editController = new BGAreaEditController(ureq, getWindowControl(), area, true);
listenTo(editController);
mainPanel.pushContent(editController.getInitialComponent());
}
}
<h4 class="b_with_small_icon_left b_group_icon">$r.translate("course.areas.title")</h4>
$r.render("areaList")
<p>$r.translate("course.areas.description")</p>
$r.render("createArea")
\ No newline at end of file
action=Aktion
course.areas.title=Lernbereich innerhalb Kurs
course.areas.description=Lernbereich erstellen
table.header.name=Name
table.header.description=Beschreibung
delete=Löschen
edit=Editieren
create.area=Lernbereich erstellen
delete.area.title=Lernbereich löschen
delete.area.description=Wollen Sie wirklich den Lernbereich "{0}" löschen?
\ No newline at end of file
action=Action
course.areas.title=Learning areas within course
course.areas.description=Create a new learning area with the button below. You can then use the learning area in the course editor to limit course element across and assign groups to learning areas.
table.header.name=Name
table.header.description=Description
delete=Delete
edit=Edit
create.area=Create learning area
\ No newline at end of file
......@@ -28,12 +28,9 @@ package org.olat.course.editor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.olat.core.commons.controllers.linkchooser.CustomLinkTreeModel;
......@@ -96,6 +93,7 @@ import org.olat.core.util.vfs.NamedContainerImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.area.CourseAreasController;
import org.olat.course.config.CourseConfig;
import org.olat.course.config.ui.courselayout.CourseLayoutHelper;
import org.olat.course.editor.PublishStepCatalog.CategoryLabel;
......@@ -136,6 +134,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
private static final String CMD_CLOSEEDITOR = "cmd.close";
private static final String CMD_PUBLISH = "pbl";
private static final String CMD_COURSEFOLDER = "cfd";
private static final String CMD_COURSEAREAS = "careas";
private static final String CMD_COURSEPREVIEW = "cprev";
private static final String CMD_KEEPCLOSED_ERROR = "keep.closed.error";
private static final String CMD_KEEPOPEN_ERROR = "keep.open.error";
......@@ -149,6 +148,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
private static final String NLS_PUBLISHED_LATEST = "published.latest";
private static final String NLS_HEADER_TOOLS = "header.tools";
private static final String NLS_COMMAND_COURSEFOLDER = "command.coursefolder";
private static final String NLS_COMMAND_COURSEAREAS = "command.courseareas";
private static final String NLS_COMMAND_COURSEPREVIEW = "command.coursepreview";
private static final String NLS_COMMAND_PUBLISH = "command.publish";
private static final String NLS_COMMAND_CLOSEEDITOR = "command.closeeditor";
......@@ -186,6 +186,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
private MoveCopySubtreeController moveCopyController;
private InsertNodeController insertNodeController;
private FolderRunController folderController;
private CourseAreasController areasController;
private DialogBoxController deleteDialogController;
private LayoutMain3ColsController columnLayoutCtr;
......@@ -305,6 +306,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
listenTo(toolC);
toolC.addHeader(translate(NLS_HEADER_TOOLS));
toolC.addLink(CMD_COURSEFOLDER, translate(NLS_COMMAND_COURSEFOLDER), CMD_COURSEFOLDER, "o_toolbox_coursefolder");
toolC.addLink(CMD_COURSEAREAS, translate(NLS_COMMAND_COURSEAREAS), CMD_COURSEAREAS, "o_toolbox_courseareas");
toolC.addLink(CMD_COURSEPREVIEW, translate(NLS_COMMAND_COURSEPREVIEW), CMD_COURSEPREVIEW, "b_toolbox_preview" );
toolC.addLink(CMD_PUBLISH, translate(NLS_COMMAND_PUBLISH), CMD_PUBLISH,"b_toolbox_publish" );
toolC.addLink(CMD_CLOSEEDITOR, translate(NLS_COMMAND_CLOSEEDITOR), null, "b_toolbox_close");
......@@ -643,13 +645,25 @@ public class EditorMainController extends MainLayoutBasicController implements G
// Folder for course with custom link model to jump to course nodes
VFSContainer namedCourseFolder = new NamedContainerImpl(translate(NLS_COURSEFOLDER_NAME), course.getCourseFolderContainer());
CustomLinkTreeModel customLinkTreeModel = new CourseInternalLinkTreeModel(course.getEditorTreeModel());
removeAsListenerAndDispose(folderController);
folderController = new FolderRunController(namedCourseFolder, true, true, true, ureq, getWindowControl(), null, customLinkTreeModel);
folderController.addLoggingResourceable(LoggingResourceable.wrap(course));
listenTo(folderController);
removeAsListenerAndDispose(cmc);
cmc = new CloseableModalController(getWindowControl(), translate(NLS_COURSEFOLDER_CLOSE), folderController.getInitialComponent());
listenTo(cmc);
cmc.activate();
} else if (event.getCommand().equals(CMD_COURSEAREAS)) {
removeAsListenerAndDispose(areasController);
areasController = new CourseAreasController(ureq, getWindowControl(), course.getCourseEnvironment().getCourseGroupManager().getCourseResource());
areasController.addLoggingResourceable(LoggingResourceable.wrap(course));
listenTo(areasController);
removeAsListenerAndDispose(cmc);
cmc = new CloseableModalController(getWindowControl(), translate(NLS_COURSEFOLDER_CLOSE), areasController.getInitialComponent());
listenTo(cmc);
cmc.activate();
} else if (event.getCommand().equals(CMD_MULTI_SP)) {
removeAsListenerAndDispose(multiSPChooserCtr);
VFSContainer rootContainer = course.getCourseEnvironment().getCourseFolderContainer();
......@@ -892,45 +906,6 @@ public class EditorMainController extends MainLayoutBasicController implements G
updateCourseStatusMessages(ureq.getLocale(), courseStatus);
}
/*
* FIXME:pb:b never used...
*/
private Map<String, List<String>> checkReferencesFor(TreeNode tn) {
final CourseEditorTreeModel cetm = CourseFactory.getCourseEditSession(ores.getResourceableId()).getEditorTreeModel();
//create a list of all nodes in the selected subtree
final Set<String> allSubTreeids = new HashSet<String>();
TreeVisitor tv = new TreeVisitor(new Visitor() {
public void visit(INode node) {
allSubTreeids.add(node.getIdent());
}
}, tn, true);
tv.visitAll();
//find all references pointing from outside the subtree into the subtree or
//on the subtree root node.
final Map<String, List<String>> allRefs = new HashMap<String, List<String>>();
tv = new TreeVisitor(new Visitor() {
public void visit(INode node) {
List referencingNodes = euce.getCourseEditorEnv().getReferencingNodeIdsFor(node.getIdent());
// subtract the inner nodes. This allows to delete a whole subtree if
// only references residing completly inside the subtree are active.
referencingNodes.removeAll(allSubTreeids);
if (referencingNodes.size() > 0) {
List<String> nodeNames = new ArrayList<String>();
for (Iterator iter = referencingNodes.iterator(); iter.hasNext();) {
String nodeId = (String) iter.next();
CourseNode cn = cetm.getCourseNode(nodeId);
nodeNames.add(cn.getShortTitle());
}
allRefs.put(node.getIdent(), nodeNames);
}
}
}, tn, true);
// traverse all nodes from the deletion startpoint
tv.visitAll();
// allRefs contains now all references, or zero if ready for delete.
return allRefs;
}
/**
* @param ureq
* @param courseStatus
......
......@@ -331,6 +331,7 @@ chelp.signLearningGroupFull=Gibt f\u00FCr die angegebene Lerngruppe den Boolean
chelp.funcLearningGroupFull=<i>isLearningGroupFull("</i>$\:chelp.string<i>")</i>
command.closeeditor=Editor schliessen
command.copynode=Kopieren
command.courseareas=Lernbereich
command.coursefolder=Ablageordner
command.coursepreview=Kursvorschau
command.deletenode=L\u00F6schen
......
......@@ -319,6 +319,7 @@ chelp.wordTrue=TRUE
command.admin.header=Collecting function
command.closeeditor=Close editor
command.copynode=Copy
command.courseareas=learning areas
command.coursefolder=Storage folder
command.coursepreview=Course preview
command.deletenode=Delete
......
......@@ -119,6 +119,11 @@ public class CourseRights implements BGRights {
trans.translate(RIGHT_STATISTICS)
};
}
public static List<String> getAvailableRights() {
List<String> available = new ArrayList<String>(rights);
return available;
}
/**
* @see org.olat.group.right.BGRights#getRights()
......
/**
* <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.course.groupsandrights;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.FormLink;
import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
import org.olat.core.gui.components.form.flexible.impl.elements.MultipleSelectionElementImpl;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.table.DefaultTableDataModel;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.WindowControl;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupService;
import org.olat.group.right.BGRightManager;
import org.olat.resource.OLATResource;
/**
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class GroupsAndRightsController extends FormBasicController {
private GroupsAndRightsDataModel tableDataModel;
private FormLink addAllLink, removeAllLink;
private FormSubmit saveLink;
private final OLATResource resource;
private final BGRightManager rightManager;
private final BusinessGroupService businessGroupService;
private static final String[] keys = {"ison"};
private static final String[] values = {""};
public GroupsAndRightsController(UserRequest ureq, WindowControl wControl, OLATResource resource) {
super(ureq, wControl, "right_list");
rightManager = CoreSpringFactory.getImpl(BGRightManager.class);
businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
this.resource = resource;
initForm(ureq);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
//group rights
FlexiTableColumnModel tableColumnModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.groups"));
tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.role"));
for(String right : CourseRights.getAvailableRights()) {
tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(right));
}
tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.remove"));
List<RightOption> groupRights = loadModel();
tableDataModel = new GroupsAndRightsDataModel(groupRights, tableColumnModel);
uifactory.addTableElement("rightList", tableDataModel, formLayout);
FormLayoutContainer buttonsLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
buttonsLayout.setRootForm(mainForm);
formLayout.add("buttons", buttonsLayout);
saveLink = uifactory.addFormSubmitButton("save", buttonsLayout);
removeAllLink = uifactory.addFormLink("remove.all", buttonsLayout, Link.BUTTON);
addAllLink = uifactory.addFormLink("add.all", buttonsLayout, Link.BUTTON);
}
private List<RightOption> loadModel() {
List<RightOption> options = new ArrayList<RightOption>();
List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, resource, 0, -1);
for(BusinessGroup group:groups) {
RightOption groupRights = new RightOption(group, "tutor");
fillCheckbox(groupRights);
FormLink removeLink = uifactory.addFormLink("remove_" + UUID.randomUUID().toString(), "table.header.remove", "table.header.remove", flc, Link.LINK);
removeLink.setUserObject(groupRights);
groupRights.setRemoveLink(removeLink);
options.add(groupRights);
}
return options;
}
private void fillCheckbox(RightOption groupRights) {
List<MultipleSelectionElement> selections = new ArrayList<MultipleSelectionElement>();
for(String right : CourseRights.getAvailableRights()) {
MultipleSelectionElement selection = createSelection(false);
selection.setUserObject(groupRights);
selections.add(selection);
}
groupRights.setRightsEl(selections);
}
private MultipleSelectionElement createSelection(boolean selected) {
String name = "cb" + UUID.randomUUID().toString().replace("-", "");
MultipleSelectionElement selection = new MultipleSelectionElementImpl(name, MultipleSelectionElementImpl.createVerticalLayout("checkbox",1));
selection.setKeysAndValues(keys, values, null);
flc.add(name, selection);
selection.select(keys[0], selected);
return selection;
}
@Override
protected void doDispose() {
//
}
@Override
protected void formOK(UserRequest ureq) {
//
System.out.println();
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(source == addAllLink) {
} else if (source == removeAllLink) {
doRemoveAllRights();
} else {
super.formInnerEvent(ureq, source, event);
}
}
private void doRemoveAllRights() {
List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, resource, 0, -1);
for(BusinessGroup group:groups) {
List<String> rights = rightManager.findBGRights(group);
}
}
private static class GroupsAndRightsDataModel extends DefaultTableDataModel<RightOption> implements FlexiTableDataModel {
private FlexiTableColumnModel columnModel;
public GroupsAndRightsDataModel(List<RightOption> options, FlexiTableColumnModel columnModel) {
super(options);
this.columnModel = columnModel;
}
@Override
public FlexiTableColumnModel getTableColumnModel() {
return columnModel;
}
@Override
public void setTableColumnModel(FlexiTableColumnModel tableColumnModel) {
columnModel = tableColumnModel;
}
@Override
public int getColumnCount() {
return columnModel.getColumnCount();
}
@Override
public Object getValueAt(int row, int col) {
RightOption groupRights = getObject(row);
if(col == 0) {
return groupRights.getGroupName();
} else if (col == 1) {
return groupRights.getRole();
} else if (col == (getColumnCount() - 1)) {
return groupRights.getRemoveLink();
}
//rights
int rightPos = col - 2;
MultipleSelectionElement rightEl = groupRights.getRightsEl().get(rightPos);
return rightEl;
}
}
private static class RightOption {
private final String groupName;
private final Long groupKey;
private final String role;
private List<MultipleSelectionElement> rightsEl;
private FormLink removeLink;
public RightOption(BusinessGroup group, String role) {
groupName = group.getName();
groupKey = group.getKey();
this.role = role;
}
public String getGroupName() {
return groupName;
}
public Long getGroupKey() {
return groupKey;
}
public String getRole() {
return role;
}
public List<MultipleSelectionElement> getRightsEl() {
return rightsEl;
}
public void setRightsEl(List<MultipleSelectionElement> rightsEl) {
this.rightsEl = rightsEl;
}
public FormLink getRemoveLink() {
return removeLink;
}
public void setRemoveLink(FormLink removeLink) {
this.removeLink = removeLink;
}
}
}
<h4 class="b_with_small_icon_left b_group_icon">
$r.translate("menu.rights")
</h4>
$r.render("rightList")
$r.render("buttons")
\ No newline at end of file
#Mon Mar 02 09:54:04 CET 2009
bgr.archive=Datenarchivierung
bgr.assess=Bewertungswerkzeug
bgr.assess=Bewertungs-Werkzeug
bgr.editor=Kurseditor
bgr.glossary=Glossarwerkzeug
bgr.groupmngt=Gruppenmanagement
bgr.glossary=Glossar-Werkzeug
bgr.groupmngt=Gruppen-Verwaltung
bgr.statistics=Statistiken
noRestriction=Keine Einschr\u00E4nkung
bgr.dbs=Kurs Datenbank
table.header.groups=Gruppe
table.header.role=Rolle
table.header.remove=Entfernen
menu.rights=Rechte
remove.all=Alle Rechte Entfernen
add.all=Alle Rechte hinzufgen
\ No newline at end of file
......@@ -27,7 +27,6 @@ import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.panel.Panel;
import org.olat.core.gui.components.tree.GenericTreeModel;
import org.olat.core.gui.components.tree.GenericTreeNode;
import org.olat.core.gui.components.tree.MenuTree;
......@@ -43,6 +42,7 @@ import org.olat.core.logging.activity.ActionType;
import org.olat.core.util.tree.TreeHelper;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.groupsandrights.GroupsAndRightsController;
import org.olat.repository.RepositoryEntry;
import org.olat.resource.accesscontrol.AccessControlModule;
import org.olat.resource.accesscontrol.ui.OrdersAdminController;
......@@ -67,6 +67,7 @@ public class MembersManagementMainController extends MainLayoutBasicController
private OrdersAdminController ordersController;
private CourseBusinessGroupListController groupsCtrl;
private MembersOverviewController membersOverviewCtrl;
private GroupsAndRightsController rightsController;
private final RepositoryEntry repoEntry;
private final AccessControlModule acModule;
......@@ -164,7 +165,11 @@ public class MembersManagementMainController extends MainLayoutBasicController
}
mainVC.put("content", ordersController.getInitialComponent());
} else if(CMD_RIGHTS.equals(cmd)) {
mainVC.put("content", new Panel("empty"));
if(rightsController == null) {
rightsController = new GroupsAndRightsController(ureq, getWindowControl(), repoEntry.getOlatResource());
listenTo(rightsController);
}
mainVC.put("content", rightsController.getInitialComponent());
}
TreeNode selTreeNode = TreeHelper.findNodeByUserObject(cmd, menuTree.getTreeModel().getRootNode());
......
......@@ -32,6 +32,8 @@ import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.choice.Choice;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.tabbedpane.TabbedPane;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
......@@ -60,7 +62,9 @@ public class BGAreaEditController extends BasicController {
// GUI components
private TabbedPane tabbedPane;
private VelocityContainer editVC, detailsTabVC, groupsTabVC;
private Link backLink;
private final VelocityContainer mainVC;
private VelocityContainer detailsTabVC, groupsTabVC;
private BGAreaFormController areaController;
private GroupsToAreaDataModel groupsDataModel;
private Choice groupsChoice;
......@@ -79,7 +83,7 @@ public class BGAreaEditController extends BasicController {
* @param wControl The window control
* @param area The business group area
*/
public BGAreaEditController(UserRequest ureq, WindowControl wControl, BGArea area) {
public BGAreaEditController(UserRequest ureq, WindowControl wControl, BGArea area, boolean back) {
super(ureq, wControl);
this.area = area;
......@@ -95,17 +99,14 @@ public class BGAreaEditController extends BasicController {
// groups tab
initAndAddGroupsTab();
// initialize main view
initEditVC();
putInitialPanel(this.editVC);
}
/**
* initialize the main velocity wrapper container
*/
private void initEditVC() {
editVC = createVelocityContainer("edit");
editVC.put("tabbedpane", tabbedPane);
editVC.contextPut("title", translate("area.edit.title", new String[] { StringEscapeUtils.escapeHtml(area.getName()).toString() }));
mainVC = createVelocityContainer("edit");
if(back) {
backLink = LinkFactory.createLinkBack(mainVC, this);
mainVC.put("backLink", backLink);
}
mainVC.put("tabbedpane", tabbedPane);
mainVC.contextPut("title", translate("area.edit.title", new String[] { StringEscapeUtils.escapeHtml(area.getName()).toString() }));
putInitialPanel(mainVC);
}
/**
......@@ -160,12 +161,14 @@ public class BGAreaEditController extends BasicController {
}
}
}
} else if (source == backLink) {
fireEvent(ureq, Event.BACK_EVENT);
}
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if (source == this.areaController) {
if (source == areaController) {
if (event == Event.DONE_EVENT) {
BGArea updatedArea = doAreaUpdate();
if (updatedArea == null) {
......@@ -173,7 +176,7 @@ public class BGAreaEditController extends BasicController {
getWindowControl().setWarning(translate("error.area.name.exists"));
} else {
area = updatedArea;
editVC.contextPut("title", translate("area.edit.title", new String[] { StringEscapeUtils.escapeHtml(area.getName()).toString() }));
mainVC.contextPut("title", translate("area.edit.title", new String[] { StringEscapeUtils.escapeHtml(area.getName()).toString() }));
}
} else if (event == Event.CANCELLED_EVENT) {
// area might have been changed, reload from db
......@@ -182,6 +185,7 @@ public class BGAreaEditController extends BasicController {
areaController = new BGAreaFormController(ureq, getWindowControl(), area, false);
listenTo(areaController);
detailsTabVC.put("areaForm", areaController.getInitialComponent());
fireEvent(ureq, event);
}
}
}
......
#if($r.available("backLink"))
$r.render("backLink")
#end
<h4 class="b_with_small_icon_left b_group_icon">
$title
</h4>
......
/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <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>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.group.ui.edit;
import java.util.List;
import org.olat.core.gui.components.table.DefaultTableDataModel;
import org.olat.group.area.BGArea;
/**
* Description:<BR>
* <P>
* Initial Date: Aug 30, 2004
*
* @author gnaegi
*/
public class AreasToGroupDataModel extends DefaultTableDataModel<BGArea> {
private List<BGArea> selectedAreas;
/**
* Constructor for the AreasToGroupDataModel
*
* @param allAreas List of all available areas
* @param selectedAreas List of all areas which are associated to the group -
* meaning where the checkbox will be checked
*/
public AreasToGroupDataModel(List<BGArea> allAreas, List<BGArea> selectedAreas) {
super(allAreas);
this.selectedAreas = selectedAreas;
}
/**
* @see org.olat.core.gui.components.table.TableDataModel#getColumnCount()
*/
public int getColumnCount() {
return 2;
}
/**
* @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int)
*/
public Object getValueAt(int row, int col) {
BGArea area = getObject(row);
if (col == 0) {
return selectedAreas.contains(area) ? Boolean.TRUE : Boolean.FALSE;
} else if (col == 1) {
return area.getName();
} else {
return "ERROR";
}
}
}
/**
* <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.group.ui.edit;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupService;
import org.olat.group.GroupLoggingAction;
import org.olat.group.area.BGArea;
import org.olat.group.area.BGAreaManager;
import org.olat.repository.RepositoryEntry;
import org.olat.util.logging.activity.LoggingResourceable;
/**
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class BusinessGroupAreasController extends FormBasicController {
private BusinessGroup businessGroup;
private final BGAreaManager areaManager;
private final BusinessGroupService businessGroupService;
public BusinessGroupAreasController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup) {
super(ureq, wControl, "tab_bgAreas");
this.businessGroup = businessGroup;
areaManager = CoreSpringFactory.getImpl(BGAreaManager.class);
businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
initForm(ureq);
updateBusinessGroup(ureq, businessGroup);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
//
}
private void updateBusinessGroup(UserRequest ureq, BusinessGroup businessGroup) {
this.businessGroup = businessGroup;
for(FormItem item: flc.getFormComponents().values()) {
flc.remove(item);
}
FormLayoutContainer choiceContainer = FormLayoutContainer.createDefaultFormLayout("areasChoice", getTranslator());
choiceContainer.setRootForm(mainForm);
flc.add("areasChoice", choiceContainer);
boolean hasAreas = false;
List<BGArea> selectedAreas = areaManager.findBGAreasOfBusinessGroup(businessGroup);
List<RepositoryEntry> entries = businessGroupService.findRepositoryEntries(Collections.singletonList(businessGroup), 0, -1);
for(RepositoryEntry entry:entries) {
List<BGArea> areas = areaManager.findBGAreasInContext(entry.getOlatResource());
if(areas.isEmpty()) continue;
hasAreas |= true;
String[] keys = new String[areas.size()];
String[] values = new String[areas.size()];
for(int i=areas.size(); i-->0; ) {
keys[i] = areas.get(i).getKey().toString();
values[i] = areas.get(i).getName();
}
MultipleSelectionElement el = uifactory.addCheckboxesVertical("repo_" + entry.getKey(), null, choiceContainer, keys, values, null, 1);
el.setLabel(entry.getDisplayname(), null, false);
el.showLabel(true);
el.setUserObject(entry.getOlatResource());
el.addActionListener(this, FormEvent.ONCHANGE);
for(String key:keys) {
for(BGArea area:selectedAreas) {
if(key.equals(area.getKey().toString())) {
el.select(key, true);
break;
}
}
}
}
flc.contextPut("noAreasFound", new Boolean(!hasAreas));
Boolean isAdmin = new Boolean(ureq.getUserSession().getRoles().isOLATAdmin() || ureq.getUserSession().getRoles().isGroupManager());
flc.contextPut("isGmAdmin", isAdmin);
}
@Override
protected void doDispose() {
//
}
@Override
protected void formOK(UserRequest ureq) {
//
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(source instanceof MultipleSelectionElement) {
MultipleSelectionElement mse = (MultipleSelectionElement)source;
Set<String> selectedAreaKeys = mse.getSelectedKeys();
Set<String> allAreaKeys = mse.getKeys();
List<BGArea> currentSelectedAreas = areaManager.findBGAreasOfBusinessGroup(businessGroup);
Map<String, BGArea> currentSelectedAreaKeys = new HashMap<String, BGArea>();
for(BGArea area:currentSelectedAreas) {
currentSelectedAreaKeys.put(area.getKey().toString(), area);
}
for(String areaKey:allAreaKeys) {
boolean selected = selectedAreaKeys.contains(areaKey);
boolean currentlySelected = currentSelectedAreaKeys.containsKey(areaKey);
if (selected && !currentlySelected) {
// add relation:
BGArea area = areaManager.loadArea(new Long(areaKey));
areaManager.addBGToBGArea(businessGroup, area);
ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_AREA_UPDATED, getClass(),
LoggingResourceable.wrap(area));
} else if (!selected && currentlySelected) {
// remove relation:
BGArea area = currentSelectedAreaKeys.get(areaKey);
areaManager.removeBGFromArea(businessGroup, area);
ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_AREA_UPDATED, getClass(),
LoggingResourceable.wrap(area));
}
}
}
super.formInnerEvent(ureq, source, event);
}
}
......@@ -91,8 +91,6 @@ public class BusinessGroupEditController extends BasicController implements Cont
private BusinessGroupToolsController collaborationToolsController;
private BusinessGroupMembersController membersController;
private BusinessGroupEditResourceController resourceController;
private BusinessGroupAreasController areasController;
private BusinessGroupRightsController rightsController;
private BusinessGroupEditAccessController tabAccessCtrl;
/**
......@@ -196,17 +194,7 @@ public class BusinessGroupEditController extends BasicController implements Cont
if(resourceController != null) {
tabbedPane.addTab(translate("group.edit.tab.resources"), resourceController.getInitialComponent());
}
//areas controller (optional)
areasController = getAreasController(ureq);
if(areasController != null) {
tabbedPane.addTab(translate("group.edit.tab.areas"), areasController.getInitialComponent());
}
rightsController = getRightsController(ureq);
if(rightsController != null) {
tabbedPane.addTab(translate("group.edit.tab.rights"), rightsController.getInitialComponent());
}
if(tabAccessCtrl != null) {
tabbedPane.addTab(translate("group.edit.tab.accesscontrol"), tabAccessCtrl.getInitialComponent());
}
......@@ -239,32 +227,6 @@ public class BusinessGroupEditController extends BasicController implements Cont
return null;
}
private BusinessGroupAreasController getAreasController(UserRequest ureq) {
if(hasResources) {
if(areasController == null) {
areasController = new BusinessGroupAreasController(ureq, getWindowControl(), currBusinessGroup);
listenTo(areasController);
}
return areasController;
}
removeAsListenerAndDispose(areasController);
areasController = null;
return null;
}
private BusinessGroupRightsController getRightsController(UserRequest ureq) {
if(hasResources) {
if(rightsController == null) {
rightsController = new BusinessGroupRightsController(ureq, getWindowControl(), currBusinessGroup);
listenTo(rightsController);
}
return rightsController;
}
removeAsListenerAndDispose(rightsController);
rightsController = null;
return null;
}
private BusinessGroupEditAccessController getAccessController(UserRequest ureq) {
if(tabAccessCtrl == null && acModule.isEnabled()) {
tabAccessCtrl = new BusinessGroupEditAccessController(ureq, getWindowControl(), currBusinessGroup);
......
/**
* <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.group.ui.edit;
import java.util.Iterator;
import java.util.List;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.choice.Choice;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.course.groupsandrights.CourseRights;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupService;
import org.olat.group.GroupLoggingAction;
import org.olat.group.right.BGRightManager;
import org.olat.group.right.BGRights;
import org.olat.util.logging.activity.LoggingResourceable;
/**
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class BusinessGroupRightsController extends BasicController {
private RightsToGroupDataModel rightDataModel;
private Choice rightsChoice;
private List<String> selectedRights;
private BGRights bgRights;
private final VelocityContainer mainVC;
private BusinessGroup businessGroup;
private final BGRightManager rightManager;
private final BusinessGroupService businessGroupService;
public BusinessGroupRightsController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup) {
super(ureq, wControl);
this.businessGroup = businessGroup;
rightManager = CoreSpringFactory.getImpl(BGRightManager.class);
businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
// Initialize available rights
bgRights = new CourseRights(ureq.getLocale());
mainVC = createVelocityContainer("tab_bgRights");
selectedRights = rightManager.findBGRights(businessGroup);
rightDataModel = new RightsToGroupDataModel(bgRights, selectedRights);
rightsChoice = new Choice("rightsChoice", getTranslator());
rightsChoice.setSubmitKey("submit");
rightsChoice.setCancelKey("cancel");
rightsChoice.setTableDataModel(this.rightDataModel);
rightsChoice.addListener(this);
mainVC.put("rightsChoice", rightsChoice);
putInitialPanel(mainVC);
}
@Override
protected void doDispose() {
//
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
if (source == rightsChoice) {
if (event == Choice.EVNT_VALIDATION_OK) {
updateGroupRightsRelations();
// do loggin
for (Iterator<String> iter = selectedRights.iterator(); iter.hasNext();) {
ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_RIGHT_UPDATED, getClass(),
LoggingResourceable.wrapBGRight(iter.next()));
}
if (selectedRights.size()==0) {
ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_RIGHT_UPDATED_EMPTY, getClass());
}
// notify current active users of this business group
BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.GROUPRIGHTS_MODIFIED_EVENT, businessGroup, null);
}
}
}
/**
* Update the rights associated to this group: remove and add rights
*/
private void updateGroupRightsRelations() {
// refresh group to prevent stale object exception and context proxy issues
businessGroup = businessGroupService.loadBusinessGroup(businessGroup);
// 1) add rights to group
List<Integer> addedRights = rightsChoice.getAddedRows();
for (Integer position: addedRights) {
String right = rightDataModel.getObject(position.intValue());
rightManager.addBGRight(right, businessGroup);
selectedRights.add(right);
}
// 2) remove rights from group
List<Integer> removedRights = rightsChoice.getRemovedRows();
for (Integer position:removedRights) {
String right = rightDataModel.getObject(position.intValue());
rightManager.removeBGRight(right, businessGroup);
selectedRights.remove(right);
}
}
}
/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <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>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.group.ui.edit;
import java.util.List;
import org.olat.core.gui.components.table.DefaultTableDataModel;
import org.olat.group.right.BGRights;
/**
* Description:<BR>
* <P>
* Initial Date: Aug 30, 2004
*
* @author gnaegi
*/
public class RightsToGroupDataModel extends DefaultTableDataModel<String> {
private List<String> selectedRights;
private BGRights bgRights;
/**
* Constructor for the RightsToGroupDataModel
*
* @param bgRights Available rights
* @param selectedRights List of all areas which are associated to the group -
* meaning where the checkbox will be checked
*/
public RightsToGroupDataModel(BGRights bgRights, List<String> selectedRights) {
super(bgRights.getRights());
this.bgRights = bgRights;
this.selectedRights = selectedRights;
}
/**
* @see org.olat.core.gui.components.table.TableDataModel#getColumnCount()
*/
public int getColumnCount() {
return 2;
}
/**
* @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int)
*/
public Object getValueAt(int row, int col) {
if (col == 0) {
return selectedRights.contains(getObject(row)) ? Boolean.TRUE : Boolean.FALSE;
} else if (col == 1) {
return bgRights.transateRight(getObject(row));
} else {
return "ERROR";
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment