diff --git a/src/main/java/de/l3s/learnweb/resource/Annotation.java b/src/main/java/de/l3s/learnweb/resource/Annotation.java index 0622d1e2a..0b720c45e 100644 --- a/src/main/java/de/l3s/learnweb/resource/Annotation.java +++ b/src/main/java/de/l3s/learnweb/resource/Annotation.java @@ -11,12 +11,13 @@ public class Annotation implements Serializable { @Serial private static final long serialVersionUID = 6321296603254649454L; + private int annotationId; private int resourceId; private int userId; private String action; private String selection; private String annotation; - private Instant timestamp; + private Instant createdAt; // cached values private transient User user; @@ -25,13 +26,12 @@ public class Annotation implements Serializable { public Annotation() { } - public Annotation(int resourceId, int userId, String action, String selection, String annotation) { - this.resourceId = resourceId; - this.userId = userId; - this.action = action; - this.selection = selection; - this.annotation = annotation; - this.timestamp = Instant.now(); + public int getAnnotationId() { + return annotationId; + } + + public void setAnnotationId(final int annotationId) { + this.annotationId = annotationId; } public int getResourceId() { @@ -74,17 +74,17 @@ public void setAction(String action) { this.action = action; } - public Instant getTimestamp() { - return timestamp; + public Instant getCreatedAt() { + return createdAt; } - public void setTimestamp(Instant timestamp) { - this.timestamp = timestamp; + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; } public User getUser() { if (null == user) { - user = Learnweb.dao().getUserDao().findByIdOrElseThrow(getUserId()); + user = Learnweb.dao().getUserDao().findByIdOrElseThrow(userId); } return user; } diff --git a/src/main/java/de/l3s/learnweb/resource/AnnotationDao.java b/src/main/java/de/l3s/learnweb/resource/AnnotationDao.java index f0b05fd94..357358999 100644 --- a/src/main/java/de/l3s/learnweb/resource/AnnotationDao.java +++ b/src/main/java/de/l3s/learnweb/resource/AnnotationDao.java @@ -17,24 +17,28 @@ import de.l3s.util.SqlHelper; +@RegisterRowMapper(AnnotationDao.AnnotationMapper.class) public interface AnnotationDao extends SqlObject, Serializable { - @RegisterRowMapper(AnnotationMapper.class) @SqlQuery("SELECT a.* FROM lw_transcript_log a JOIN lw_resource USING(resource_id) WHERE user_id IN() and deleted = 0 ORDER BY user_id, created_at DESC") List findLogsByUserIds(@BindList("userIds") Collection userIds); + @SqlQuery("SELECT a.* FROM lw_resource_annotation a WHERE resource_id = ?") + List findAllByResourceId(int resourceId); + default void save(Annotation annotation) { - if (annotation.getTimestamp() == null) { - annotation.setTimestamp(Instant.now()); + if (annotation.getCreatedAt() == null) { + annotation.setCreatedAt(Instant.now()); } LinkedHashMap params = new LinkedHashMap<>(); + params.put("annotation_id", annotation.getAnnotationId()); params.put("resource_id", annotation.getResourceId()); params.put("user_id", annotation.getUserId()); - params.put("action", annotation.getAction()); + params.put("action", SqlHelper.toNullable(annotation.getAction())); params.put("selection", SqlHelper.toNullable(annotation.getSelection())); params.put("annotation", SqlHelper.toNullable(annotation.getAnnotation())); - params.put("created_at", annotation.getTimestamp()); + params.put("created_at", annotation.getCreatedAt()); SqlHelper.handleSave(getHandle(), "lw_resource_annotation", params).execute(); } @@ -43,12 +47,13 @@ class AnnotationMapper implements RowMapper { @Override public Annotation map(final ResultSet rs, final StatementContext ctx) throws SQLException { Annotation log = new Annotation(); + log.setAnnotationId(rs.getInt("annotation_id")); log.setResourceId(rs.getInt("resource_id")); log.setUserId(rs.getInt("user_id")); log.setAction(rs.getString("action")); - log.setSelection(rs.getString("words_selected")); - log.setAnnotation(rs.getString("user_annotation")); - log.setTimestamp(rs.getTimestamp("created_at").toInstant()); + log.setSelection(rs.getString("selection")); + log.setAnnotation(rs.getString("annotation")); + log.setCreatedAt(rs.getTimestamp("created_at").toInstant()); return log; } } diff --git a/src/main/java/de/l3s/learnweb/resource/Resource.java b/src/main/java/de/l3s/learnweb/resource/Resource.java index 4bd20ae02..42d639856 100644 --- a/src/main/java/de/l3s/learnweb/resource/Resource.java +++ b/src/main/java/de/l3s/learnweb/resource/Resource.java @@ -557,9 +557,9 @@ public void setTypeFromFormat(String format) { return; } - if (format.equals("text/html") || format.equals("application/xhtml+xml")) { + if (StringUtils.equalsAny(format, "text/html", "application/xhtml+xml")) { this.type = ResourceType.website; - } else if (format.startsWith("text/") || StringUtils.equalsAny(format, "application/json", "application/xml")) { + } else if (format.startsWith("text/") || StringUtils.equalsAny(format, "application/json", "application/xml", "application/sql")) { this.type = ResourceType.text; } else if (format.startsWith("image/")) { this.type = ResourceType.image; diff --git a/src/main/java/de/l3s/learnweb/resource/ResourceAnnotationBean.java b/src/main/java/de/l3s/learnweb/resource/ResourceAnnotationBean.java index 2d5136e57..f19720c7e 100644 --- a/src/main/java/de/l3s/learnweb/resource/ResourceAnnotationBean.java +++ b/src/main/java/de/l3s/learnweb/resource/ResourceAnnotationBean.java @@ -1,8 +1,11 @@ package de.l3s.learnweb.resource; +import java.io.IOException; import java.io.Serial; import java.io.Serializable; +import java.nio.file.Files; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -12,10 +15,15 @@ import jakarta.inject.Inject; import jakarta.inject.Named; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.omnifaces.util.Beans; import org.omnifaces.util.Faces; import org.omnifaces.util.Messages; import org.primefaces.PrimeFaces; +import com.google.gson.Gson; + import de.l3s.learnweb.beans.ApplicationBean; import de.l3s.learnweb.beans.BeanAssert; import de.l3s.util.NlpHelper; @@ -25,8 +33,10 @@ public class ResourceAnnotationBean extends ApplicationBean implements Serializable { @Serial private static final long serialVersionUID = -6100755972011969429L; + private static final Logger log = LogManager.getLogger(ResourceAnnotationBean.class); private static final Pattern SPACES = Pattern.compile("\\s+"); + private Resource resource; @Inject private AnnotationDao annotationDao; @@ -34,6 +44,31 @@ public class ResourceAnnotationBean extends ApplicationBean implements Serializa @PostConstruct public void onLoad() { BeanAssert.authorized(isLoggedIn()); + + resource = Beans.getInstance(ResourceDetailBean.class).getResource(); + } + + public void convert() { + if (resource.getType() == ResourceType.text) { + if (resource.getMainFile() != null) { + try { + String lines = Files.readString(resource.getMainFile().getActualFile().toPath()); + lines = lines.replace("\n", "
"); + resource.setTranscript(lines); + resource.save(); + return; + } catch (IOException e) { + log.error("Error reading file while extracting transcript", e); + } + } + } + + addGrowl(FacesMessage.SEVERITY_ERROR, "Unfortunately, we could not extract the text from the file."); + } + + public String getAnnotationsAsJson() { + List annotations = annotationDao.findAllByResourceId(resource.getId()); + return new Gson().toJson(annotations); } /** @@ -46,8 +81,6 @@ public void commandSaveAnnotation(final Resource resource) { resource.setTranscript(annotatedText); resource.save(); - annotationDao.save(new Annotation(resource.getId(), getUser().getId(), "save transcript", null, null)); - getUser().clearCaches(); addGrowl(FacesMessage.SEVERITY_INFO, "Changes_saved"); } @@ -63,8 +96,6 @@ public void commandCommitAnnotation(final Resource resource) { resource.setReadOnlyTranscript(true); resource.save(); - annotationDao.save(new Annotation(resource.getId(), getUser().getId(), "submit transcript", null, null)); - getUser().clearCaches(); addGrowl(FacesMessage.SEVERITY_INFO, "Annotation Submitted"); } @@ -76,7 +107,13 @@ public void commandLogAnnotation(final Resource resource) { BeanAssert.authorized(resource.canAnnotateResource(getUser())); Map params = Faces.getRequestParameterMap(); - annotationDao.save(new Annotation(resource.getId(), getUser().getId(), params.get("action"), params.get("selection"), params.get("annotation"))); + Annotation annotation = new Annotation(); + annotation.setResourceId(resource.getId()); + annotation.setUserId(getUser().getId()); + annotation.setAction(params.get("action")); + annotation.setSelection(params.get("selection")); + annotation.setAnnotation(params.get("annotation")); + annotationDao.save(annotation); } /** diff --git a/src/main/java/de/l3s/learnweb/resource/ResourceType.java b/src/main/java/de/l3s/learnweb/resource/ResourceType.java index 1c6b2e4d2..fe8decacc 100644 --- a/src/main/java/de/l3s/learnweb/resource/ResourceType.java +++ b/src/main/java/de/l3s/learnweb/resource/ResourceType.java @@ -14,9 +14,9 @@ public enum ResourceType { audio, pdf, website, - spreadsheet, - presentation, - document, + spreadsheet, // onlyoffice + presentation, // onlyoffice + document, // onlyoffice file, // applications, archives, etc // learnweb types diff --git a/src/main/resources/db/migration/V14__Add_Resource_Annotation.sql b/src/main/resources/db/migration/V14__Add_Resource_Annotation.sql index 319f2b1d8..15f257cfd 100644 --- a/src/main/resources/db/migration/V14__Add_Resource_Annotation.sql +++ b/src/main/resources/db/migration/V14__Add_Resource_Annotation.sql @@ -1,8 +1,9 @@ CREATE TABLE IF NOT EXISTS `lw_resource_annotation` ( + `annotation_id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, `resource_id` INT(10) UNSIGNED NOT NULL, `user_id` INT(10) UNSIGNED NOT NULL, `action` CHAR(25) NOT NULL, `selection` LONGTEXT NOT NULL, - `annotation` MEDIUMTEXT NOT NULL, + `annotation` MEDIUMTEXT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() ); diff --git a/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_survey.xhtml b/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_survey.xhtml index 8f78b43dc..f17483d8d 100644 --- a/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_survey.xhtml +++ b/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_survey.xhtml @@ -2,7 +2,7 @@ xmlns:h="jakarta.faces.html" xmlns:f="jakarta.faces.core" xmlns:ui="jakarta.faces.facelets" - xmlns:c="http://xmlns.jcp.org/jsp/jstl/core" + xmlns:c="jakarta.tags.core" xmlns:p="http://primefaces.org/ui"> diff --git a/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_text.xhtml b/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_text.xhtml index 581a5ed22..b76fc2535 100644 --- a/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_text.xhtml +++ b/src/main/webapp/WEB-INF/templates/blocks/resourceview/embedded_text.xhtml @@ -1,73 +1,80 @@ + xmlns:faces="jakarta.faces" + xmlns:ui="jakarta.faces.facelets"> + + + + + - - - - + + + - - - - - - -
- - -
-
+ + +
+ +
+ + + +
-
-                
-            
+
- - - - - - + + +
+ + +
-
- - -
-
-
+
+ + +
+
+
- - const annotator = new Annotator('#annotated_text', { - readOnly: false, - lastNoteId: 0, - editNoteFormSelector: '#update_note_form', - noteContextMenu: [ - { action: 'add-annotation', icon: 'fa-plus', name: '#{msg.add_annotation}' }, - { action: 'edit-annotation', icon: 'fa-pencil-alt', name: '#{msg.edit_annotation}' }, - { action: 'delete-annotation', icon: 'fa-minus', name: '#{msg.delete_annotation}' }, - { action: 'add-wordnet-definition', icon: 'fa-language', name: '#{msg.add_wordnet_definition}' }, - { action: 'delete-selection', icon: 'fa-trash', name: '#{msg.delete_selection}' }, - { action: 'colorpicker', icon: 'fa-folder', name: '#{msg.color}' } - ], - locale: { - deleteSelection: '#{msg.delete_selection}' - } - }); - + + const annotator = new Annotator('#annotated_text', { + readOnly: false, + lastNoteId: 0, + editNoteFormSelector: '#update_note_form', + noteContextMenu: [ + { action: 'add-annotation', icon: 'fa-plus', name: '#{msg.add_annotation}' }, + { action: 'edit-annotation', icon: 'fa-pencil-alt', name: '#{msg.edit_annotation}' }, + { action: 'delete-annotation', icon: 'fa-minus', name: '#{msg.delete_annotation}' }, + { action: 'add-wordnet-definition', icon: 'fa-language', name: '#{msg.add_wordnet_definition}' }, + { action: 'delete-selection', icon: 'fa-trash', name: '#{msg.delete_selection}' }, + { action: 'colorpicker', icon: 'fa-folder', name: '#{msg.color}' } + ], + annotations: [ + #{resourceAnnotationBean.getAnnotationsAsJson()} + ], + locale: { + deleteSelection: '#{msg.delete_selection}' + } + }); + +
+
diff --git a/src/main/webapp/WEB-INF/templates/blocks/survey/answer.xhtml b/src/main/webapp/WEB-INF/templates/blocks/survey/answer.xhtml index dfe997643..425aee9fa 100644 --- a/src/main/webapp/WEB-INF/templates/blocks/survey/answer.xhtml +++ b/src/main/webapp/WEB-INF/templates/blocks/survey/answer.xhtml @@ -2,8 +2,8 @@ xmlns:h="jakarta.faces.html" xmlns:f="jakarta.faces.core" xmlns:ui="jakarta.faces.facelets" - xmlns:c="http://xmlns.jcp.org/jsp/jstl/core" - xmlns:jsf="http://xmlns.jcp.org/jsf" + xmlns:c="jakarta.tags.core" + xmlns:faces="jakarta.faces" xmlns:p="http://primefaces.org/ui">
@@ -24,13 +24,13 @@ -

+

-
+
-
+
@@ -52,13 +52,13 @@
-
+
-
+
diff --git a/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_edit.xhtml b/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_edit.xhtml index d8861f63e..715fcf1a6 100644 --- a/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_edit.xhtml +++ b/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_edit.xhtml @@ -2,9 +2,9 @@ xmlns:ui="jakarta.faces.facelets" xmlns:h="jakarta.faces.html" xmlns:f="jakarta.faces.core" - xmlns:jsf="http://xmlns.jcp.org/jsf" + xmlns:faces="jakarta.faces" xmlns:p="http://primefaces.org/ui" - xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"> + xmlns:fn="jakarta.tags.functions"> @@ -38,7 +38,7 @@
-
+
diff --git a/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_view.xhtml b/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_view.xhtml index fb0d6d970..b2a51311f 100644 --- a/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_view.xhtml +++ b/src/main/webapp/WEB-INF/templates/blocks/survey/snippet_question_view.xhtml @@ -2,7 +2,7 @@ xmlns:ui="jakarta.faces.facelets" xmlns:h="jakarta.faces.html" xmlns:f="jakarta.faces.core" - xmlns:jsf="http://xmlns.jcp.org/jsf" + xmlns:faces="jakarta.faces" xmlns:p="http://primefaces.org/ui"> @@ -10,17 +10,17 @@ -

+

-
+
-
+
diff --git a/src/main/webapp/lw/admin/detailed_transcript_log.xhtml b/src/main/webapp/lw/admin/detailed_transcript_log.xhtml index 27dcdd4dd..b3093fc21 100644 --- a/src/main/webapp/lw/admin/detailed_transcript_log.xhtml +++ b/src/main/webapp/lw/admin/detailed_transcript_log.xhtml @@ -102,8 +102,8 @@ - - + + diff --git a/src/main/webapp/lw/ted_transcript.xhtml b/src/main/webapp/lw/ted_transcript.xhtml index 5c8c74c6d..328a506f8 100644 --- a/src/main/webapp/lw/ted_transcript.xhtml +++ b/src/main/webapp/lw/ted_transcript.xhtml @@ -125,8 +125,8 @@
- - + +