diff --git a/src/main/java/org/olat/course/condition/interpreter/ConditionInterpreter.java b/src/main/java/org/olat/course/condition/interpreter/ConditionInterpreter.java index 318ac1de2c657d6b7eb9ef285dda5d6b53bbabc2..4d0d1f751b81e8002c2b0da6eb6250a48fc69f77 100644 --- a/src/main/java/org/olat/course/condition/interpreter/ConditionInterpreter.java +++ b/src/main/java/org/olat/course/condition/interpreter/ConditionInterpreter.java @@ -38,6 +38,7 @@ import org.olat.course.condition.interpreter.score.GetPassedFunction; import org.olat.course.condition.interpreter.score.GetPassedWithCourseIdFunction; import org.olat.course.condition.interpreter.score.GetScoreFunction; import org.olat.course.condition.interpreter.score.GetScoreWithCourseIdFunction; +import org.olat.course.db.interpreter.GetUserCourseDBFunction; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.run.userview.UserCourseEnvironment; @@ -121,6 +122,7 @@ public class ConditionInterpreter { eaf = new EvalAttributeFunction(userCourseEnv, EvalAttributeFunction.FUNCTION_TYPE_ATTRIBUTE_STARTS_WITH); env.addFunction(eaf.name, eaf); env.addFunction(GetUserPropertyFunction.name, new GetUserPropertyFunction(userCourseEnv)); + env.addFunction(GetUserCourseDBFunction.name, new GetUserCourseDBFunction(userCourseEnv)); env.addFunction(HasLanguageFunction.name, new HasLanguageFunction(userCourseEnv)); env.addFunction(InInstitutionFunction.name, new InInstitutionFunction(userCourseEnv)); env.addFunction(IsCourseCoachFunction.name, new IsCourseCoachFunction(userCourseEnv)); diff --git a/src/main/java/org/olat/course/condition/interpreter/OnlyGroupConditionInterpreter.java b/src/main/java/org/olat/course/condition/interpreter/OnlyGroupConditionInterpreter.java index 4883a4bea7ec310078967787e9a4582e826a2dff..0c9f6152cbce30482527eea85fe1a8b7dcd8c135 100644 --- a/src/main/java/org/olat/course/condition/interpreter/OnlyGroupConditionInterpreter.java +++ b/src/main/java/org/olat/course/condition/interpreter/OnlyGroupConditionInterpreter.java @@ -30,6 +30,7 @@ import org.olat.course.condition.interpreter.score.GetPassedFunction; import org.olat.course.condition.interpreter.score.GetPassedWithCourseIdFunction; import org.olat.course.condition.interpreter.score.GetScoreFunction; import org.olat.course.condition.interpreter.score.GetScoreWithCourseIdFunction; +import org.olat.course.db.interpreter.GetUserCourseDBFunction; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.run.userview.UserCourseEnvironment; @@ -80,6 +81,7 @@ public class OnlyGroupConditionInterpreter extends ConditionInterpreter{ env.addFunction("hasAttribute", new DummyBooleanFunction(userCourseEnv)); env.addFunction("isInAttribute", new DummyBooleanFunction(userCourseEnv)); env.addFunction(GetUserPropertyFunction.name, new DummyStringFunction(userCourseEnv)); + env.addFunction(GetUserCourseDBFunction.name, new DummyStringFunction(userCourseEnv)); env.addFunction(HasLanguageFunction.name, new DummyBooleanFunction(userCourseEnv)); env.addFunction(InInstitutionFunction.name, new DummyBooleanFunction(userCourseEnv)); env.addFunction(IsCourseCoachFunction.name, new DummyBooleanFunction(userCourseEnv)); diff --git a/src/main/java/org/olat/course/db/CourseDBEntry.java b/src/main/java/org/olat/course/db/CourseDBEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..6515b47c11467c386d2f69fb8d55fb323efde8c3 --- /dev/null +++ b/src/main/java/org/olat/course/db/CourseDBEntry.java @@ -0,0 +1,47 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db; + +import org.olat.core.id.Identity; + +/** + * + * Description:<br> + * TODO: srosse Class Description for CourseDBEntry + * + * <P> + * Initial Date: 7 apr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +public interface CourseDBEntry { + + public Long getCourseKey(); + + public Identity getIdentity(); + + public String getCategory(); + + public String getName(); + + public Object getValue(); + + public void setValue(Object value); +} diff --git a/src/main/java/org/olat/course/db/CourseDBManager.java b/src/main/java/org/olat/course/db/CourseDBManager.java new file mode 100644 index 0000000000000000000000000000000000000000..eb20ea91cb9aa3644f315d90e6d942c3e67bd207 --- /dev/null +++ b/src/main/java/org/olat/course/db/CourseDBManager.java @@ -0,0 +1,59 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db; + +import java.util.List; + +import org.olat.core.CoreSpringFactory; +import org.olat.core.configuration.AbstractOLATModule; +import org.olat.core.configuration.ConfigOnOff; +import org.olat.core.id.Identity; +import org.olat.course.ICourse; + +/** + * + * Description:<br> + * TODO: srosse Class Description for CourseDBManager + * + * <P> + * Initial Date: 7 apr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +public abstract class CourseDBManager extends AbstractOLATModule implements ConfigOnOff { + + public static CourseDBManager getInstance() { + return (CourseDBManager)CoreSpringFactory.getBean("courseDBManager"); + } + + public abstract boolean isEnabled(); + + public abstract void reset(ICourse course, String category); + + public abstract CourseDBEntry getValue(ICourse course, Identity identity, String category, String name); + + public abstract CourseDBEntry getValue(Long courseResourceId, Identity identity, String category, String name); + + public abstract boolean deleteValue(ICourse course, Identity identity, String category, String name); + + public abstract CourseDBEntry setValue(ICourse course, Identity identity, String category, String name, Object value); + + public abstract List<CourseDBEntry> getValues(ICourse course, Identity identity, String category, String name); +} diff --git a/src/main/java/org/olat/course/db/CourseDBMediaResource.java b/src/main/java/org/olat/course/db/CourseDBMediaResource.java new file mode 100644 index 0000000000000000000000000000000000000000..709b575e518972fba069658d5ccb15242212d31e --- /dev/null +++ b/src/main/java/org/olat/course/db/CourseDBMediaResource.java @@ -0,0 +1,63 @@ +package org.olat.course.db; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import javax.servlet.http.HttpServletResponse; + +import org.olat.core.gui.media.MediaResource; +import org.olat.core.util.StringHelper; + +/** + * + * Description:<br> + * + * <P> + * Initial Date: 13 déc. 2011 <br> + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class CourseDBMediaResource implements MediaResource { + + private final String charset; + private final String fileName; + private final byte[] content; + + public CourseDBMediaResource(String charset, String fileName, byte[] content) { + this.charset = charset; + this.fileName = fileName; + this.content = content; + } + + @Override + public String getContentType() { + return "application/vnd.ms-excel; charset=" + charset; + } + + @Override + public Long getSize() { + return content == null ? new Long(0) : content.length; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(content); + } + + @Override + public Long getLastModified() { + return -1l; + } + + @Override + public void prepare(HttpServletResponse hres) { + hres.setHeader("Content-Disposition","filename=\"" + StringHelper.urlEncodeISO88591(fileName) + "\""); + hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(fileName)); + + } + + @Override + public void release() { + // + } +} diff --git a/src/main/java/org/olat/course/db/CustomDBAddController.java b/src/main/java/org/olat/course/db/CustomDBAddController.java new file mode 100644 index 0000000000000000000000000000000000000000..acac3fb88c3497916a62755d18269613630f1499 --- /dev/null +++ b/src/main/java/org/olat/course/db/CustomDBAddController.java @@ -0,0 +1,81 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.TextElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; + +/** + * + * Description:<br> + * TODO: srosse Class Description for CustomDBAddController + * + * <P> + * Initial Date: 7 apr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +public class CustomDBAddController extends FormBasicController { + + private TextElement categoryEl; + + public CustomDBAddController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl); + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + categoryEl = uifactory.addTextElement("category", "customDb.category", 32, "", formLayout); + + final FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttonLayout", getTranslator()); + formLayout.add(buttonLayout); + uifactory.addFormSubmitButton("ok", buttonLayout); + uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl()); + } + + public String getCategory() { + return categoryEl.getValue(); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + fireEvent(ureq, Event.DONE_EVENT); + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + +} diff --git a/src/main/java/org/olat/course/db/CustomDBController.java b/src/main/java/org/olat/course/db/CustomDBController.java new file mode 100644 index 0000000000000000000000000000000000000000..ec457032e0aa5845dd5714aa6a70d3a4325bf6b6 --- /dev/null +++ b/src/main/java/org/olat/course/db/CustomDBController.java @@ -0,0 +1,278 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +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.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.link.Link; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.helpers.Settings; +import org.olat.core.id.User; +import org.olat.core.id.UserConstants; +import org.olat.core.util.ExportUtil; +import org.olat.core.util.StringHelper; +import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.coordinate.SyncerExecutor; +import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.nodes.CourseNode; +import org.olat.course.properties.CoursePropertyManager; +import org.olat.course.tree.CourseEditorTreeNode; +import org.olat.properties.Property; +import org.olat.restapi.security.RestSecurityHelper; +import org.olat.user.UserManager; + +/** + * + * Description:<br> + * Controller to manage a custom DB, export, delete... + * + * <P> + * Initial Date: 7 apr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class CustomDBController extends FormBasicController { + + private List<FormLink> resetDbs = new ArrayList<FormLink>(); + private List<FormLink> deleteDbs = new ArrayList<FormLink>(); + private List<FormLink> exportDbs = new ArrayList<FormLink>(); + private FormLayoutContainer dbListLayout; + + private final Long courseKey; + + public CustomDBController(UserRequest ureq, WindowControl wControl, Long courseKey) { + super(ureq, wControl, LAYOUT_VERTICAL); + this.courseKey = courseKey; + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + setFormTitle("customDb.custom_db"); + + dbListLayout = FormLayoutContainer.createDefaultFormLayout("dbListLayout", getTranslator()); + formLayout.add(dbListLayout); + updateDBList(dbListLayout); + } + + public void updateUI() { + FormItem[] items = new FormItem[dbListLayout.getFormComponents().size()]; + items = dbListLayout.getFormComponents().values().toArray(items); + for(FormItem item:items) { + dbListLayout.remove(item); + } + initialPanel.setDirty(true); + updateDBList(dbListLayout); + } + + private void updateDBList(FormItemContainer formLayout) { + ICourse course = CourseFactory.loadCourse(courseKey); + CoursePropertyManager cpm = course.getCourseEnvironment().getCoursePropertyManager(); + CourseNode rootNode = ((CourseEditorTreeNode)course.getEditorTreeModel().getRootNode()).getCourseNode(); + Property p = cpm.findCourseNodeProperty(rootNode, null, null, CustomDBMainController.CUSTOM_DB); + if(p != null && p.getTextValue() != null) { + String[] dbs = p.getTextValue().split(":"); + + int count = 0; + for(String db:dbs) { + if(!StringHelper.containsNonWhitespace(db)) continue; + + uifactory.addStaticExampleText("category_" + count, "customDb.category", db, formLayout); + String url = Settings.getServerContextPathURI() + RestSecurityHelper.SUB_CONTEXT + "/repo/courses/" + courseKey + "/db/" + db; + uifactory.addStaticExampleText("url_" + count, "customDb.url", url, formLayout); + + final FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttonLayout_" + count, getTranslator()); + formLayout.add(buttonLayout); + + FormLink resetDb = uifactory.addFormLink("db-reset_" + count, "customDb.reset", "customDb.reset", buttonLayout, Link.BUTTON_SMALL); + resetDb.setUserObject(db); + FormLink deleteDb = uifactory.addFormLink("db-delete_" + count, "delete", "delete", buttonLayout, Link.BUTTON_SMALL); + deleteDb.setUserObject(db); + FormLink exportDb = uifactory.addFormLink("db-export_" + count, "customDb.export", "customDb.export", buttonLayout, Link.BUTTON_SMALL); + exportDb.setUserObject(db); + + resetDbs.add(resetDb); + deleteDbs.add(deleteDb); + exportDbs.add(exportDb); + + count++; + } + } + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + //do nothing as default + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (resetDbs.contains(source)) { + resetDb((String)source.getUserObject()); + } else if (deleteDbs.contains(source)) { + deleteDb((String)source.getUserObject()); + } else if (exportDbs.contains(source)) { + exportDb(ureq, (String)source.getUserObject()); + } + } + + private void resetDb(String category) { + CourseDBManager dbManager = CourseDBManager.getInstance(); + ICourse course = CourseFactory.loadCourse(courseKey); + dbManager.reset(course, category); + } + + private void deleteDb(String category) { + CourseDBManager dbManager = CourseDBManager.getInstance(); + ICourse course = CourseFactory.loadCourse(courseKey); + dbManager.reset(course, category); + deleteCustomDb(course, category); + updateUI(); + } + + private void exportDb(UserRequest ureq, String category) { + ICourse course = CourseFactory.loadCourse(courseKey); + byte[] content = getDbsContent(course.getCourseTitle(), category); + if(content == null) { + showError("customDb.export.failed"); + } else { + UserManager um = UserManager.getInstance(); + String charset = um.getUserCharset(ureq.getIdentity()); + String fileName = ExportUtil.createFileNameWithTimeStamp("DBS_" + course.getCourseTitle(), "xls"); + MediaResource export = new CourseDBMediaResource(charset, fileName, content); + ureq.getDispatchResult().setResultingMediaResource(export); + } + } + + private byte[] getDbsContent(String courseTitle, String category) { + ICourse course = CourseFactory.loadCourse(courseKey); + List<CourseDBEntry> content = CourseDBManager.getInstance().getValues(course, null, category, null); + + Workbook wb = new HSSFWorkbook(); + CellStyle headerCellStyle = getHeaderCellStyle(wb); + Sheet exportSheet = wb.createSheet(courseTitle); + + //create the headers + Row headerRow = exportSheet.createRow(0); + Cell cell = headerRow.createCell(0); + cell.setCellValue(translate("customDb.category")); + cell.setCellStyle(headerCellStyle); + + cell = headerRow.createCell(1); + cell.setCellValue(translate("customDb.entry.identity")); + cell.setCellStyle(headerCellStyle); + + cell = headerRow.createCell(2); + cell.setCellValue(translate("customDb.entry.name")); + cell.setCellStyle(headerCellStyle); + + cell = headerRow.createCell(3); + cell.setCellValue(translate("customDb.entry.value")); + cell.setCellStyle(headerCellStyle); + + int count = 0; + for (CourseDBEntry entry:content) { + User user = entry.getIdentity().getUser(); + String name = user.getProperty(UserConstants.FIRSTNAME, null) + " " + user.getProperty(UserConstants.LASTNAME, null); + + Row dataRow = exportSheet.createRow(++count); + Cell dataCell = dataRow.createCell(0); + dataCell.setCellValue(entry.getCategory()); + + dataCell = dataRow.createCell(1); + dataCell.setCellValue(name); + + if(StringHelper.containsNonWhitespace(entry.getName())) { + dataCell = dataRow.createCell(2); + dataCell.setCellValue(entry.getName()); + } + + if(entry.getValue() != null) { + dataCell = dataRow.createCell(3); + dataCell.setCellValue(entry.getValue().toString()); + } + } + + try { + ByteArrayOutputStream fos = new ByteArrayOutputStream(); + wb.write(fos); + fos.flush(); + return fos.toByteArray(); + } catch (IOException e) { + logError("", e); + } + return null; + } + + private CellStyle getHeaderCellStyle(final Workbook wb) { + CellStyle cellStyle = wb.createCellStyle(); + Font boldFont = wb.createFont(); + boldFont.setBoldweight(Font.BOLDWEIGHT_BOLD); + cellStyle.setFont(boldFont); + return cellStyle; + } + + private void deleteCustomDb(final ICourse course, final String category) { + CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(course, new SyncerExecutor() { + @Override + public void execute() { + CoursePropertyManager cpm = course.getCourseEnvironment().getCoursePropertyManager(); + CourseNode rootNode = ((CourseEditorTreeNode)course.getEditorTreeModel().getRootNode()).getCourseNode(); + Property p = cpm.findCourseNodeProperty(rootNode, null, null, CustomDBMainController.CUSTOM_DB); + if(p != null && p.getTextValue() != null) { + String[] dbs = p.getTextValue().split(":"); + StringBuilder currentDbs = new StringBuilder(); + for(String db:dbs) { + if(!db.equals(category)) { + currentDbs.append(db).append(':'); + } + } + p.setTextValue(currentDbs.toString()); + cpm.updateProperty(p); + } + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/db/CustomDBMainController.java b/src/main/java/org/olat/course/db/CustomDBMainController.java new file mode 100644 index 0000000000000000000000000000000000000000..0329ffed93a6f4e52c7077a90a035912cb76aca3 --- /dev/null +++ b/src/main/java/org/olat/course/db/CustomDBMainController.java @@ -0,0 +1,150 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.tree.GenericTreeNode; +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.generic.closablewrapper.CloseableModalController; +import org.olat.core.gui.control.generic.layout.GenericMainController; +import org.olat.core.gui.control.generic.tool.ToolController; +import org.olat.core.gui.control.generic.tool.ToolFactory; +import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.coordinate.SyncerExecutor; +import org.olat.course.ICourse; +import org.olat.course.nodes.CourseNode; +import org.olat.course.properties.CoursePropertyManager; +import org.olat.course.tree.CourseEditorTreeNode; +import org.olat.properties.Property; + +/** + * + * Description:<br> + * TODO: srosse Class Description for CustomDBMainController + * + * <P> + * Initial Date: 7 avr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +public class CustomDBMainController extends GenericMainController { + + public static final String CUSTOM_DB = "custom_db"; + + private ICourse course; + private ToolController toolC; + + private CustomDBController dbController; + private CustomDBAddController addController; + private CloseableModalController cmc; + + public CustomDBMainController(UserRequest ureq, WindowControl windowControl, ICourse course) { + super(ureq, windowControl); + this.course = course; + + // Tool and action box + toolC = ToolFactory.createToolController(getWindowControl()); + listenTo(toolC); + toolC.addHeader(translate("tool.name")); + toolC.addLink("cmd.new_db", translate("command.new_db"), null, "b_new"); + toolC.addLink("cmd.close", translate("command.closedb"), null, "b_toolbox_close"); + setToolController(toolC); + + //set main node + GenericTreeNode root = new GenericTreeNode(); + root.setTitle(translate("main.menu.title")); + root.setAltText(translate("main.menu.title.alt")); + root.setUserObject("dbs"); + addChildNodeToPrepend(root); + + init(ureq); + } + + @Override + protected void doDispose() { + // controllers disposed by BasicController: + } + + private void disposeAddController() { + removeAsListenerAndDispose(addController); + removeAsListenerAndDispose(cmc); + addController = null; + cmc = null; + } + + /** + * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, + * org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event) + */ + public void event(UserRequest ureq, Controller source, Event event) { + if (source == toolC) { + if (event.getCommand().equals("cmd.close")) { + doDispose(); + fireEvent(ureq, Event.DONE_EVENT); + } else if (event.getCommand().equals("cmd.new_db")) { + removeAsListenerAndDispose(addController); + addController = new CustomDBAddController(ureq, getWindowControl()); + listenTo(addController); + removeAsListenerAndDispose(cmc); + cmc = new CloseableModalController(getWindowControl(), translate("close"), addController.getInitialComponent()); + listenTo(cmc); + cmc.activate(); + } + } else if (source == addController) { + if(event == Event.DONE_EVENT) { + String category = addController.getCategory(); + addCustomDb(category); + dbController.updateUI(); + } + cmc.deactivate(); + disposeAddController(); + } + } + + @Override + protected Controller handleOwnMenuTreeEvent(Object uobject, UserRequest ureq) { + if("dbs".equals(uobject)) { + dbController = new CustomDBController(ureq, getWindowControl(), course.getResourceableId()); + return dbController; + } + return null; + } + + private void addCustomDb(final String category) { + CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(course, new SyncerExecutor() { + @Override + public void execute() { + CoursePropertyManager cpm = course.getCourseEnvironment().getCoursePropertyManager(); + CourseNode rootNode = ((CourseEditorTreeNode)course.getEditorTreeModel().getRootNode()).getCourseNode(); + Property p = cpm.findCourseNodeProperty(rootNode, null, null, CUSTOM_DB); + if(p == null) { + p = cpm.createCourseNodePropertyInstance(rootNode, null, null, CUSTOM_DB, null, null, null, category); + cpm.saveProperty(p); + } else { + String currentDbs = p.getTextValue(); + p.setTextValue(currentDbs + ":" + category); + cpm.updateProperty(p); + } + } + }); + } +} diff --git a/src/main/java/org/olat/course/db/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/db/_i18n/LocalStrings_de.properties new file mode 100644 index 0000000000000000000000000000000000000000..52c6721b44fd91f83cbc1e0f46152d0683a63ad1 --- /dev/null +++ b/src/main/java/org/olat/course/db/_i18n/LocalStrings_de.properties @@ -0,0 +1,16 @@ +#Mon Mar 02 09:54:04 CET 2009 +command.new_db=Neue Datenbank erstellen +command.closedb=Schliessen +customDb.custom_db=Datenbank +customDb.create=Erstellen +customDb.reset=Zurücksetzen +customDb.export=Exportieren +customDb.category=Name +customDb.entry.identity=Benutzer +customDb.entry.name=Name +customDb.entry.value=Wert +customDb.url=URL +main.menu.title=Datenbanken +main.menu.title.alt=Kurs Datenbanken +tool.name=Datenbanken +customDb.export.failed= \ No newline at end of file diff --git a/src/main/java/org/olat/course/db/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/db/_i18n/LocalStrings_en.properties new file mode 100644 index 0000000000000000000000000000000000000000..aacbccf15b98cdcaea30c130a4d719adc6876a78 --- /dev/null +++ b/src/main/java/org/olat/course/db/_i18n/LocalStrings_en.properties @@ -0,0 +1,15 @@ +#Thu May 26 09:44:01 CEST 2011 +command.closedb=Close +command.new_db=Create new database +customDb.category=Name +customDb.create=Create +customDb.custom_db=Database +customDb.entry.identity=User +customDb.entry.name=Name +customDb.entry.value=Value +customDb.export=Export +customDb.reset=Reset +customDb.url=URL +main.menu.title=Databases +main.menu.title.alt=Course databases +tool.name=Databases diff --git a/src/main/java/org/olat/course/db/_spring/coursedbContext.xml b/src/main/java/org/olat/course/db/_spring/coursedbContext.xml new file mode 100644 index 0000000000000000000000000000000000000000..07af74c1081fbfbbae2fd60d1b475301ff52354f --- /dev/null +++ b/src/main/java/org/olat/course/db/_spring/coursedbContext.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + + <bean id="courseDBManager" class="org.olat.course.db.impl.CourseDBManagerImpl"> + <property name="persistedProperties"> + <bean class="org.olat.core.configuration.PersistedProperties" scope="prototype" init-method="init" destroy-method="destroy" + depends-on="coordinatorManager,org.olat.core.util.WebappHelper"> + <constructor-arg index="0" ref="coordinatorManager"/> + <constructor-arg index="1" ref="courseDBManager" /> + </bean> + </property> + </bean> + + <!-- default configuration --> + <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> + <property name="targetObject" ref="courseDBManager" /> + <property name="targetMethod" value="init" /> + <property name="arguments"> + <value> + enabled=${course.db.enabled} + </value> + </property> + </bean> + + <bean id="courseDbWebService" class="org.olat.course.db.restapi.CourseDbWebService" /> + + <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> + <property name="targetObject" ref="org.olat.restapi.support.RestRegistrationService" /> + <property name="targetMethod" value="addSingleton" /> + <property name="arguments" ref="courseDbWebService"/> + </bean> + +</beans> \ No newline at end of file diff --git a/src/main/java/org/olat/course/db/impl/CourseDBEntryImpl.hbm.xml b/src/main/java/org/olat/course/db/impl/CourseDBEntryImpl.hbm.xml new file mode 100644 index 0000000000000000000000000000000000000000..e9c116c529791c2767b8f656f10b8775f8c6e654 --- /dev/null +++ b/src/main/java/org/olat/course/db/impl/CourseDBEntryImpl.hbm.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!DOCTYPE hibernate-mapping PUBLIC + "-//Hibernate/Hibernate Mapping DTD//EN" + "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> + +<hibernate-mapping default-lazy="false"> + + <class name="org.olat.course.db.impl.CourseDBEntryImpl" table="o_co_db_entry"> + <id name="key" type="long" column="id" unsaved-value="null"> + <generator class="hilo" /> + </id> + + <version name="version" access="field" column="version"/> + <property name="lastModified" column="lastmodified" type="timestamp" /> + <property name="creationDate" column="creationdate" type="timestamp" /> + + <property name="courseKey" type="long" column="courseid" /> + <many-to-one + name="identity" + class="org.olat.basesecurity.IdentityImpl" + outer-join="false" + cascade="none"/> + + <property name="category" type="string" column="category" length="32" index="o_co_db_cat_idx"/> + <property name="name" type="string" column="name" not-null="true" index="o_co_db_name_idx"/> + <property name="floatValue" type="float" column="floatvalue" /> + <property name="longValue" type="long" column="longvalue" /> + <property name="stringValue" type="string" column="stringvalue" /> + <property name="textValue" type="text" column="textvalue" /> + </class> + +</hibernate-mapping> diff --git a/src/main/java/org/olat/course/db/impl/CourseDBEntryImpl.java b/src/main/java/org/olat/course/db/impl/CourseDBEntryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ccff8ffe318e6738a6100883bd702d2dcc0f8ec4 --- /dev/null +++ b/src/main/java/org/olat/course/db/impl/CourseDBEntryImpl.java @@ -0,0 +1,151 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db.impl; + +import java.util.Date; + +import org.olat.core.commons.persistence.PersistentObject; +import org.olat.core.id.Identity; +import org.olat.course.db.CourseDBEntry; + +/** + * + * Description:<br> + * TODO: srosse Class Description for CourseDBEntryImpl + * + * <P> + * Initial Date: 7 avr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +public class CourseDBEntryImpl extends PersistentObject implements CourseDBEntry { + + private static final long serialVersionUID = -6487632477815812235L; + + private Long courseKey; + private Identity identity; + + private String category; + private String name; + private Float floatValue; + private Long longValue; + private String stringValue; + private String textValue; + private Date lastModified; + + public Long getCourseKey() { + return courseKey; + } + + public void setCourseKey(Long courseKey) { + this.courseKey = courseKey; + } + + public Identity getIdentity() { + return identity; + } + + public void setIdentity(Identity identity) { + this.identity = identity; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public Object getValue() { + if(getStringValue() != null) { + return getStringValue(); + } else if (getLongValue() != null) { + return getLongValue(); + } else if (getFloatValue() != null) { + return getFloatValue(); + } + return null; + } + + @Override + public void setValue(Object value) { + if(value instanceof Long) { + setLongValue((Long)value); + } else if (value instanceof Float) { + setFloatValue((Float)value); + } else if (value instanceof String) { + setStringValue((String)value); + } + } + + public Float getFloatValue() { + return floatValue; + } + + public void setFloatValue(Float floatValue) { + this.floatValue = floatValue; + } + + public Long getLongValue() { + return longValue; + } + + public void setLongValue(Long longValue) { + this.longValue = longValue; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public String getTextValue() { + return textValue; + } + + public void setTextValue(String textValue) { + this.textValue = textValue; + } + + public Date getLastModified() { + return lastModified; + } + + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } + + + + +} diff --git a/src/main/java/org/olat/course/db/impl/CourseDBManagerImpl.java b/src/main/java/org/olat/course/db/impl/CourseDBManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..82d87ed7d0326685c4dda8366754bcf2368ad4b2 --- /dev/null +++ b/src/main/java/org/olat/course/db/impl/CourseDBManagerImpl.java @@ -0,0 +1,192 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db.impl; + +import java.util.List; + +import org.hibernate.FlushMode; +import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.commons.persistence.DBQuery; +import org.olat.core.configuration.PersistedProperties; +import org.olat.core.id.Identity; +import org.olat.core.util.StringHelper; +import org.olat.core.util.event.GenericEventListener; +import org.olat.course.ICourse; +import org.olat.course.db.CourseDBEntry; +import org.olat.course.db.CourseDBManager; + +/** + * + * Description:<br> + * TODO: srosse Class Description for CourseDBManagerImpl + * + * <P> + * Initial Date: 7 apr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +public class CourseDBManagerImpl extends CourseDBManager implements GenericEventListener { + + private static final String ENABLED = "enabled"; + private boolean enabled; + + public CourseDBManagerImpl() { + // + } + + @Override + public void init() { + //enabled/disabled + String enabledObj = getStringPropertyValue(ENABLED, true); + if(StringHelper.containsNonWhitespace(enabledObj)) { + enabled = "true".equals(enabledObj); + } + } + + @Override + protected void initDefaultProperties() { + enabled = getBooleanConfigParameter("enabled", false); + } + + @Override + protected void initFromChangedProperties() { + init(); + } + + @Override + public void setPersistedProperties(PersistedProperties persistedProperties) { + this.moduleConfigProperties = persistedProperties; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reset(ICourse course, String category) { + StringBuilder sb = new StringBuilder(); + sb.append("delete from ").append(CourseDBEntryImpl.class.getName()) + .append(" entry where entry.courseKey=:courseKey"); + if(StringHelper.containsNonWhitespace(category)) { + sb.append(" and entry.category=:category"); + } + + DBQuery query = DBFactory.getInstance().createQuery(sb.toString()); + query.setLong("courseKey", course.getResourceableId()); + if(StringHelper.containsNonWhitespace(category)) { + query.setString("category", category); + } + query.executeUpdate(FlushMode.AUTO); + } + + @Override + public CourseDBEntry getValue(ICourse course, Identity identity, String category, String name) { + CourseDBEntry entry = loadEntry(course.getResourceableId(), identity, category, name); + return entry; + } + + @Override + public CourseDBEntry getValue(Long courseResourceableId, Identity identity, String category, String name) { + CourseDBEntry entry = loadEntry(courseResourceableId, identity, category, name); + return entry; + } + + @Override + public List<CourseDBEntry> getValues(ICourse course, Identity identity, String category, String name) { + StringBuilder sb = new StringBuilder(); + sb.append("select entry from ").append(CourseDBEntryImpl.class.getName()) + .append(" entry where entry.courseKey=:courseKey"); + if(identity != null) { + sb.append(" and entry.identity=:identity"); + } + if(StringHelper.containsNonWhitespace(category)) { + sb.append(" and entry.category=:category"); + } + if(StringHelper.containsNonWhitespace(name)) { + sb.append(" and entry.name=:name"); + } + + DBQuery query = DBFactory.getInstance().createQuery(sb.toString()); + query.setLong("courseKey", course.getResourceableId()); + if(identity != null) { + query.setEntity("identity", identity); + } + if(StringHelper.containsNonWhitespace(category)) { + query.setString("category", category); + } + if(StringHelper.containsNonWhitespace(name)) { + query.setString("name", name); + } + return query.list(); + } + + @Override + public CourseDBEntry setValue(ICourse course, Identity identity, String category, String name, Object value) { + CourseDBEntryImpl entry = loadEntry(course.getResourceableId(), identity, category, name); + if(entry == null) { + entry = new CourseDBEntryImpl(); + entry.setCourseKey(course.getResourceableId()); + entry.setIdentity(identity); + entry.setName(name); + entry.setCategory(category); + } + entry.setValue(value); + DBFactory.getInstance().saveObject(entry); + return entry; + } + + private CourseDBEntryImpl loadEntry(Long courseResourceableId, Identity identity, String category, String name) { + StringBuilder sb = new StringBuilder(); + sb.append("select entry from ").append(CourseDBEntryImpl.class.getName()) + .append(" entry where") + .append(" entry.identity=:identity and entry.courseKey=:courseKey ") + .append(" and entry.name=:named and entry.category=:category"); + + DBQuery query = DBFactory.getInstance().createQuery(sb.toString()); + query.setString("named", name); + query.setString("category", category); + query.setEntity("identity", identity); + query.setLong("courseKey", courseResourceableId); + + List<CourseDBEntryImpl> entries = query.list(); + if(entries.isEmpty()) { + return null; + } + return entries.get(0); + } + + @Override + public boolean deleteValue(ICourse course, Identity identity, String category, String name) { + StringBuilder sb = new StringBuilder(); + sb.append("delete from ").append(CourseDBEntryImpl.class.getName()) + .append(" entry where entry.identity=:identity and entry.courseKey=:courseKey ") + .append(" and entry.name=:name and entry.category=:category"); + + DBQuery query = DBFactory.getInstance().createQuery(sb.toString()); + query.setString("name", name); + query.setString("category", category); + query.setEntity("identity", identity); + query.setLong("courseKey", course.getResourceableId()); + + int rowAffected = query.executeUpdate(FlushMode.AUTO); + return rowAffected > 0; + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/db/interpreter/GetUserCourseDBFunction.java b/src/main/java/org/olat/course/db/interpreter/GetUserCourseDBFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..2757d529a7948b73e95a82271c2a95c9a6b9f320 --- /dev/null +++ b/src/main/java/org/olat/course/db/interpreter/GetUserCourseDBFunction.java @@ -0,0 +1,125 @@ +/** + * <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.db.interpreter; + +import org.olat.core.id.Identity; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.course.condition.interpreter.AbstractFunction; +import org.olat.course.condition.interpreter.ArgumentParseException; +import org.olat.course.db.CourseDBEntry; +import org.olat.course.db.CourseDBManager; +import org.olat.course.editor.CourseEditorEnv; +import org.olat.course.run.userview.UserCourseEnvironment; + +/** + * + * Description:<br> + * + * <P> + * Initial Date: 13 déc. 2011 <br> + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class GetUserCourseDBFunction extends AbstractFunction { + + private static OLog log = Tracing.createLoggerFor(GetUserCourseDBFunction.class); + + public static final String name = "getUserCourseDBValue"; + + /** + * Constructor + * @param userCourseEnv + */ + public GetUserCourseDBFunction(UserCourseEnvironment userCourseEnv) { + super(userCourseEnv); + } + + /** + * @see org.olat.course.condition.interpreter.AbstractFunction#call(java.lang.Object[]) + */ + @Override + public Object call(Object[] inStack) { + /* + * argument check + */ + if (inStack.length > 2) { + return handleException(new ArgumentParseException(ArgumentParseException.NEEDS_FEWER_ARGUMENTS, name, "", "error.fewerargs", + "solution.providetwo.attrvalue")); + } else if (inStack.length < 1) { + return handleException(new ArgumentParseException(ArgumentParseException.NEEDS_MORE_ARGUMENTS, name, + "", "error.moreargs", "solution.providetwo.attrvalue")); + } + /* + * argument type check + */ + if (!(inStack[0] instanceof String)) { + return handleException(new ArgumentParseException(ArgumentParseException.WRONG_ARGUMENT_FORMAT, + name, "", "error.argtype.attributename", "solution.example.name.infunction")); + } + + CourseEditorEnv cev = getUserCourseEnv().getCourseEditorEnv(); + if (cev != null) { + // return emtyp string to continue with condition evaluation test + return defaultValue(); + } + + CourseDBManager courseDbManager = CourseDBManager.getInstance(); + + Identity ident = getUserCourseEnv().getIdentityEnvironment().getIdentity(); + + String category = null; + String name = null; + if(inStack.length == 1) { + category = null; + name = (String) inStack[1]; + } else if (inStack.length == 2) { + category = (String) inStack[0]; + name = (String) inStack[1]; + } + + Long courseId = getUserCourseEnv().getCourseEnvironment().getCourseResourceableId(); + Object value; + try { + CourseDBEntry entry = courseDbManager.getValue(courseId, ident, category, name); + if(entry == null) { + return defaultValue(); + } + value = entry.getValue(); + if(value == null) { + return defaultValue(); + } + } catch (Exception e) { + log.error("", e); + return defaultValue(); + } + return value.toString(); + } + + /** + * @see org.olat.course.condition.interpreter.AbstractFunction#defaultValue() + */ + @Override + protected Object defaultValue() { + return ""; + } + +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/db/restapi/CourseDbWebService.java b/src/main/java/org/olat/course/db/restapi/CourseDbWebService.java new file mode 100644 index 0000000000000000000000000000000000000000..f4bebcee809d52468f538222740eecf042ff715d --- /dev/null +++ b/src/main/java/org/olat/course/db/restapi/CourseDbWebService.java @@ -0,0 +1,296 @@ +/** + * 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) frentix GmbH<br> + * http://www.frentix.com<br> + * <p> + */ +package org.olat.course.db.restapi; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.olat.core.gui.UserRequest; +import org.olat.core.id.Roles; +import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.db.CourseDBEntry; +import org.olat.course.db.CourseDBManager; +import org.olat.restapi.security.RestSecurityHelper; +import org.olat.restapi.support.vo.KeyValuePair; + +/** + * Description:<br> + * Access the custom dbs of a course + * + * <P> + * Initial Date: 7 apr. 2010 <br> + * @author srosse, stephane.rosse@frentix.com + */ +@Path("repo/courses/{courseId}/db/{category}") +public class CourseDbWebService { + + private static final String VERSION = "1.0"; + + /** + * Retrieves the version of the Course DB Web Service. + * @response.representation.200.mediaType text/plain + * @response.representation.200.doc The version of this specific Web Service + * @response.representation.200.example 1.0 + * @return + */ + @GET + @Path("version") + @Produces(MediaType.TEXT_PLAIN) + public Response getVersion() { + return Response.ok(VERSION).build(); + } + + /** + * Retrieve all values of the authenticated user + * @response.representation.200.qname {http://www.example.com}keyValuePair + * @response.representation.200.mediaType application/xml, application/json + * @response.representation.200.doc All the values in the course + * @response.representation.200.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_KEYVALUEVOes} + * @param courseId The course resourceable's id + * @param category The name of the database + * @param request The HTTP request + * @return + */ + @GET + @Path("values") + @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response getValues(@PathParam("courseId") Long courseId, @PathParam("category") String category, @Context HttpServletRequest request) { + ICourse course = CourseFactory.loadCourse(courseId); + UserRequest ureq = RestSecurityHelper.getUserRequest(request); + List<CourseDBEntry> entries = CourseDBManager.getInstance().getValues(course, ureq.getIdentity(), category, null); + + KeyValuePair[] pairs = new KeyValuePair[entries.size()]; + int count=0; + for(CourseDBEntry entry:entries) { + Object value = entry.getValue(); + pairs[count++] = new KeyValuePair(entry.getName(), value == null ? "" : value.toString()); + } + return Response.ok(pairs).build(); + } + + /** + * Put a new value for an authenticated user. + * @response.representation.qname {http://www.example.com}keyValuePair + * @response.representation.mediaType application/xml, application/json + * @response.representation.doc the key value pair is saved on the db + * @response.representation.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_KEYVALUEVOes} + * @response.representation.200.doc the key value pair is saved on the db + * @param courseId The course resourceable's id + * @param category The name of the database + * @param pair The key value pair + * @param request The HTTP request + * @return + */ + @PUT + @Path("values") + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response putValues(@PathParam("courseId") Long courseId, @PathParam("category") String category, KeyValuePair pair, @Context HttpServletRequest request) { + return internPutValues(courseId, category, pair, request); + } + + /** + * Update a value for an authenticated user. + * @response.representation.qname {http://www.example.com}keyValuePair + * @response.representation.mediaType application/xml, application/json + * @response.representation.doc the key value pair is saved on the db + * @response.representation.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_KEYVALUEVOes} + * @response.representation.200.doc the key value pair is saved on the db + * @param courseId The course resourceable's id + * @param category The name of the database + * @param pair The key value pair + * @param request The HTTP request + * @return + */ + @POST + @Path("values") + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response postValues(@PathParam("courseId") Long courseId, @PathParam("category") String category, KeyValuePair pair, @Context HttpServletRequest request) { + return internPutValues(courseId, category, pair, request); + } + + /** + * Retrieve a value of an authenticated user. + * @response.representation.200.qname {http://www.example.com}keyValuePair + * @response.representation.200.mediaType application/xml, application/json + * @response.representation.200.doc The value in the course + * @response.representation.200.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_KEYVALUEVO} + * @response.representation.404.doc The entry cannot be found + * @param courseId The course resourceable's id + * @param category The name of the database + * @parma name The name of the key value pair + * @param request The HTTP request + * @return + */ + @GET + @Path("values/{name}") + @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response getValue(@PathParam("courseId") Long courseId, @PathParam("category") String category, @PathParam("name") String name, @Context HttpServletRequest request) { + ICourse course = CourseFactory.loadCourse(courseId); + UserRequest ureq = RestSecurityHelper.getUserRequest(request); + CourseDBEntry entry = CourseDBManager.getInstance().getValue(course, ureq.getIdentity(), category, name); + if(entry == null) { + return Response.serverError().status(Status.NOT_FOUND).build(); + } + Object value = entry.getValue(); + KeyValuePair pair = new KeyValuePair(name, value == null ? "" : value.toString()); + return Response.ok(pair).build(); + } + + /** + * Retrieve a value of an authenticated user. + * @response.representation.200.qname {http://www.example.com}keyValuePair + * @response.representation.200.mediaType text/plain, text/html + * @response.representation.200.doc A value of the course + * @response.representation.200.example Green + * @response.representation.404.doc The entry cannot be found + * @param courseId The course resourceable's id + * @param category The name of the database + * @param name The name of the key value pair + * @param request The HTTP request + * @return + */ + @GET + @Path("values/{name}") + @Produces({MediaType.TEXT_PLAIN, MediaType.TEXT_HTML}) + public Response getValuePlain(@PathParam("courseId") Long courseId, @PathParam("category") String category, @PathParam("name") String name, + @Context HttpServletRequest request) { + ICourse course = CourseFactory.loadCourse(courseId); + UserRequest ureq = RestSecurityHelper.getUserRequest(request); + CourseDBEntry entry = CourseDBManager.getInstance().getValue(course, ureq.getIdentity(), category, name); + if(entry == null) { + return Response.serverError().status(Status.NOT_FOUND).build(); + } + Object value = entry.getValue(); + String val = value == null ? "" : value.toString(); + return Response.ok(val).build(); + } + + /** + * Put a new value for an authenticated user. + * @response.representation.200.doc The value is saved in the course + * @param courseId The course resourceable's id + * @param category The name of the database + * @param name The name of the key value pair + * @param value The value of the key value pair + * @param request The HTTP request + * @return + */ + @PUT + @Path("values/{name}") + public Response putValue(@PathParam("courseId") Long courseId, @PathParam("category") String category, @PathParam("name") String name, + @QueryParam("value") String value, @Context HttpServletRequest request) { + return internPutValue(courseId, category, name, value, request); + } + + /** + * Update a value for an authenticated user. + * @response.representation.200.doc The value is saved in the course + * @param courseId The course resourceable's id + * @param category The name of the database + * @param name The name of the key value pair + * @param val The value of the key value pair + * @param request The HTTP request + * @return + */ + @POST + @Path("values/{name}") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public Response formValue(@PathParam("courseId") Long courseId, @PathParam("category") String category, @PathParam("name") String name, + @FormParam("val") String value, @Context HttpServletRequest request){ + return internPutValue(courseId, category, name, value, request); + } + + /** + * Delete a value for an authenticated user. + * @response.representation.200.doc the key value pair is remove from the db + * @response.representation.401.doc The roles of the authenticated user are not sufficient + * @response.representation.404.doc The entry cannot be found + * @param courseId The course resourceable's id + * @param category The name of the database + * @param name The name of the key value pair + * @param request The HTTP request + * @return + */ + @DELETE + @Path("values/{name}") + public Response deleteValue(@PathParam("courseId") Long courseId, @PathParam("category") String category, + @PathParam("name") String name, @Context HttpServletRequest request) { + Roles roles = RestSecurityHelper.getRoles(request); + if(roles.isAuthor() || roles.isOLATAdmin()) { + ICourse course = CourseFactory.loadCourse(courseId); + UserRequest ureq = RestSecurityHelper.getUserRequest(request); + boolean ok = CourseDBManager.getInstance().deleteValue(course, ureq.getIdentity(), category, name); + if(ok) { + return Response.ok().build(); + } + return Response.serverError().status(Status.NOT_FOUND).build(); + } + return Response.serverError().status(Status.UNAUTHORIZED).build(); + } + + /** + * Fallbakc method for the browsers + * @response.representation.200.doc the key value pair is remove from the db + * @response.representation.401.doc The roles of the authenticated user are not sufficient + * @response.representation.404.doc The entry cannot be found + * @param courseId The course resourceable's id + * @param category The name of the database + * @param name The name of the key value pair + * @param request The HTTP request + * @return + */ + @POST + @Path("values/{name}/delete") + public Response deleteValuePost(@PathParam("courseId") Long courseId, @PathParam("category") String category, + @PathParam("name") String name, @Context HttpServletRequest request) { + return deleteValue(courseId, category, name, request); + } + + private Response internPutValues(Long courseId, String category, KeyValuePair pair, HttpServletRequest request) { + return internPutValue(courseId, category, pair.getKey(), pair.getValue(), request); + } + + private Response internPutValue(Long courseId, String category, String name, Object value, HttpServletRequest request) { + ICourse course = CourseFactory.loadCourse(courseId); + UserRequest ureq = RestSecurityHelper.getUserRequest(request); + CourseDBEntry entry = CourseDBManager.getInstance().setValue(course, ureq.getIdentity(), category, name, value); + if(entry == null) { + return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); + } + return Response.ok().build(); + } +} diff --git a/src/main/java/org/olat/course/groupsandrights/CourseRights.java b/src/main/java/org/olat/course/groupsandrights/CourseRights.java index c55238ad8764092137bbaacb69006f5e5e9fe8f7..a4eb12c56c621115eb3d0048f9c95bb65f22d4e9 100644 --- a/src/main/java/org/olat/course/groupsandrights/CourseRights.java +++ b/src/main/java/org/olat/course/groupsandrights/CourseRights.java @@ -59,6 +59,9 @@ public class CourseRights implements BGRights { public static final String RIGHT_GLOSSARY = BGRightManager.BG_RIGHT_PREFIX + "glossary"; /** course right for statistics tool */ public static final String RIGHT_STATISTICS = BGRightManager.BG_RIGHT_PREFIX + "statistics"; + //fxdiff: right for course db + /** course right for custom dbs */ + public static final String RIGHT_DB = BGRightManager.BG_RIGHT_PREFIX + "dbs"; private static List rights; private Translator trans; @@ -72,6 +75,8 @@ public class CourseRights implements BGRights { rights.add(RIGHT_ASSESSMENT); rights.add(RIGHT_GLOSSARY); rights.add(RIGHT_STATISTICS); + //fxdiff: right for course db + rights.add(RIGHT_DB); } diff --git a/src/main/java/org/olat/course/run/RunMainController.java b/src/main/java/org/olat/course/run/RunMainController.java index c6ce6a52806bd4cf168a113148790ea09bf949c0..cd4a238a62ccba1a02323eb5f5bfa996eeb72613 100644 --- a/src/main/java/org/olat/course/run/RunMainController.java +++ b/src/main/java/org/olat/course/run/RunMainController.java @@ -101,6 +101,8 @@ import org.olat.course.assessment.UserEfficiencyStatement; import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.config.CourseConfig; import org.olat.course.config.CourseConfigEvent; +import org.olat.course.db.CourseDBManager; +import org.olat.course.db.CustomDBMainController; import org.olat.course.editor.PublishEvent; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.groupsandrights.CourseRights; @@ -502,6 +504,7 @@ public class RunMainController extends MainLayoutBasicController implements Gene courseRightsCache.put(CourseRights.RIGHT_ASSESSMENT, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_ASSESSMENT))); courseRightsCache.put(CourseRights.RIGHT_GLOSSARY, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_GLOSSARY))); courseRightsCache.put(CourseRights.RIGHT_STATISTICS, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_STATISTICS))); + courseRightsCache.put(CourseRights.RIGHT_DB, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_DB))); } /** @@ -841,7 +844,15 @@ public class RunMainController extends MainLayoutBasicController implements Gene listenTo(currentToolCtr); all.setContent(currentToolCtr.getInitialComponent()); } else throw new OLATSecurityException("clicked statistic, but no according right"); - } else if (cmd.equals("archiver")) { + //fxdiff: open a panel to manage the course dbs + } else if (cmd.equals("customDb")) { + if (hasCourseRight(CourseRights.RIGHT_DB) || isCourseAdmin) { + currentToolCtr = new CustomDBMainController(ureq, getWindowControl(), course); + listenTo(currentToolCtr); + all.setContent(currentToolCtr.getInitialComponent()); + } else throw new OLATSecurityException("clicked dbs, but no according right"); + + }else if (cmd.equals("archiver")) { if (hasCourseRight(CourseRights.RIGHT_ARCHIVING) || isCourseAdmin) { currentToolCtr = new ArchiverMainController(ureq, getWindowControl(), course, new IArchiverCallback() { public boolean mayArchiveQtiResults() { @@ -1148,6 +1159,10 @@ public class RunMainController extends MainLayoutBasicController implements Gene if (hasCourseRight(CourseRights.RIGHT_STATISTICS) || isCourseAdmin) { myTool.addLink("statistic", translate("command.openstatistic")); } + //fxdiff: enable the course db menu item + if (CourseDBManager.getInstance().isEnabled() && (hasCourseRight(CourseRights.RIGHT_DB) || isCourseAdmin)) { + myTool.addLink("customDb", translate("command.opendb")); + } // /* diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index 06c2d954b6df3b01c57a7f245e6538d88c76f085..c460bf10ff25e143f29d0dcedd93765c269e49fc 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -320,6 +320,8 @@ minimalhome.ext.calendar=true minimalhome.ext.mysettings=true minimalhome.ext.portal=true +course.db.enabled=false + ######################################################################## #Top navigation configuration ########################################################################