From 3ac768735d077da88409ba7dc46279b09b49cbe9 Mon Sep 17 00:00:00 2001 From: aboeckle <alexander.boeckle@frentix.com> Date: Wed, 2 Sep 2020 13:42:56 +0200 Subject: [PATCH] OO-4854: Alphabetical sorting for catalog --- .../form/flexible/elements/FormToggle.java | 6 + .../org/olat/repository/CatalogEntry.java | 37 ++++- .../org/olat/repository/RepositoryModule.java | 66 ++++++-- .../repository/manager/CatalogManager.java | 157 ++++++++++++------ .../repository/model/CatalogEntryImpl.java | 28 +++- .../ui/admin/CatalogAdminController.java | 63 +++++-- .../ui/admin/_i18n/LocalStrings_de.properties | 10 +- .../ui/admin/_i18n/LocalStrings_en.properties | 12 +- .../catalog/CatalogEntryEditController.java | 41 ++++- .../ui/catalog/CatalogNodeController.java | 55 +++--- .../catalog/CatalogNodeManagerController.java | 154 +++++++++++------ .../repository/ui/catalog/_content/node.html | 3 + .../catalog/_i18n/LocalStrings_de.properties | 11 +- .../catalog/_i18n/LocalStrings_en.properties | 9 +- .../org/olat/upgrade/OLATUpgrade_15_2_3.java | 136 +++++++++++++++ .../_spring/databaseUpgradeContext.xml | 10 +- .../olat/upgrade/_spring/upgradeContext.xml | 3 +- .../database/mysql/alter_15_2_x_to_15_2_3.sql | 2 + .../oracle/alter_15_2_x_to_15_2_3.sql | 2 + .../postgresql/alter_15_2_x_to_15_2_3.sql | 2 + 20 files changed, 629 insertions(+), 178 deletions(-) create mode 100644 src/main/java/org/olat/upgrade/OLATUpgrade_15_2_3.java create mode 100644 src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql create mode 100644 src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql create mode 100644 src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormToggle.java b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormToggle.java index 7f7fd4a7db4..0e18f0efa4a 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormToggle.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormToggle.java @@ -61,4 +61,10 @@ public void setToggledOnCSS(String toggledOnCSS); */ public void setToggledOffCSS(String toggledOffCSS); +/** + * set the i18n key + * @param i18n + */ +public void setI18nKey(String i18n); + } \ No newline at end of file diff --git a/src/main/java/org/olat/repository/CatalogEntry.java b/src/main/java/org/olat/repository/CatalogEntry.java index 0760c0c9e4a..04fbb1abf41 100644 --- a/src/main/java/org/olat/repository/CatalogEntry.java +++ b/src/main/java/org/olat/repository/CatalogEntry.java @@ -186,6 +186,42 @@ public interface CatalogEntry extends CatalogEntryRef, CreateInfo, Persistable, * @param shortTitle */ public void setShortTitle(String shortTitle); + + /** + * Returns whether new entries should be added on top, bottom or alphabetically + * 0 - Alphabetically + * 1 - On top + * 2 - On bottom + * + * @return + */ + public Integer getEntryAddPosition(); + + /** + * Set how new entries should be added + * 0 - Alphabetically + * 1 - On top + * 2 - On bottom + */ + public void setEntryAddPosition(Integer addEntryPosition); + + /** + * Returns whether new categories should be added on top, bottom or alphabetically + * 0 - Alphabetically + * 1 - On top + * 2 - On bottom + * + * @return + */ + public Integer getCategoryAddPosition(); + + /** + * Set how new entries should be added + * 0 - Alphabetically + * 1 - On top + * 2 - On bottom + */ + public void setCategoryAddPosition(Integer addCategoryPosition); public enum OrderBy { @@ -197,6 +233,5 @@ public interface CatalogEntry extends CatalogEntryRef, CreateInfo, Persistable, tiles, list, compact, - } } \ No newline at end of file diff --git a/src/main/java/org/olat/repository/RepositoryModule.java b/src/main/java/org/olat/repository/RepositoryModule.java index f40ed446bdd..763e833e1b9 100644 --- a/src/main/java/org/olat/repository/RepositoryModule.java +++ b/src/main/java/org/olat/repository/RepositoryModule.java @@ -55,7 +55,9 @@ public class RepositoryModule extends AbstractSpringModule { private static final String CATALOG_SITE_ENABLED = "site.catalog.enable"; private static final String CATALOG_ENABLED = "catalog.enable"; private static final String CATALOG_BROWSING_ENABLED = "catalog.brwosing.enable"; - private static final String CATALOG_ADD_AT_LAST = "catalog.add.last"; + private static final String CATALOG_MULTI_SELECT_ENABLED = "catalog.multi.select.enable"; + private static final String CATALOG_ADD_ENTRY_POSITION = "catalog.add.entry.position"; + private static final String CATALOG_ADD_CATEGORY_POSITION = "catalog.add.catalog.position"; private static final String MYCOURSES_SEARCH_ENABLED = "mycourses.search.enabled"; private static final String MYCOURSES_ALL_RESOURCES_ENABLED = "mycourses.all.resources.enabled"; @@ -76,8 +78,12 @@ public class RepositoryModule extends AbstractSpringModule { private boolean catalogEnabled; @Value("${repo.catalog.browsing.enable}") private boolean catalogBrowsingEnabled; - @Value("${catalog.add.last:true}") - private boolean catalogAddLast; + @Value("${catalog.multi.select.enable:false}") + private boolean catalogMultiSelectEnabled; + @Value("${catalog.add.entry.position:0}") + private int catalogAddEntryPosition; + @Value("${catalog.add.category.position:0}") + private int catalogAddCategoryPosition; @Value("${repo.managed}") private boolean managedRepositoryEntries; @@ -161,11 +167,16 @@ public class RepositoryModule extends AbstractSpringModule { if(StringHelper.containsNonWhitespace(catalogRepo)) { catalogEnabled = "true".equals(catalogRepo); } - + String myCourses = getStringPropertyValue(CATALOG_BROWSING_ENABLED, true); if(StringHelper.containsNonWhitespace(myCourses)) { catalogBrowsingEnabled = "true".equals(myCourses); } + + String catalogMultiSelect = getStringPropertyValue(CATALOG_MULTI_SELECT_ENABLED, true); + if(StringHelper.containsNonWhitespace(catalogMultiSelect)) { + catalogMultiSelectEnabled = "true".equals(catalogMultiSelect); + } String myCoursesSearch = getStringPropertyValue(MYCOURSES_SEARCH_ENABLED, true); if(StringHelper.containsNonWhitespace(myCoursesSearch)) { @@ -211,11 +222,12 @@ public class RepositoryModule extends AbstractSpringModule { if(StringHelper.containsNonWhitespace(taxonomyTreeKeyObj)) { taxonomyTreeKey = taxonomyTreeKeyObj; } - - String catalogAddLastObj = getStringPropertyValue(CATALOG_ADD_AT_LAST, true); - if(StringHelper.containsNonWhitespace(taxonomyTreeKeyObj)) { - catalogAddLast = "true".equals(catalogAddLastObj); - } + + // 0 -> Alphabetical + // 1 -> Add on top + // 2 -> Add on bottom + catalogAddEntryPosition = getIntPropertyValue(CATALOG_ADD_ENTRY_POSITION, catalogAddEntryPosition); + catalogAddCategoryPosition = getIntPropertyValue(CATALOG_ADD_CATEGORY_POSITION, catalogAddCategoryPosition); } /** @@ -278,14 +290,32 @@ public class RepositoryModule extends AbstractSpringModule { catalogBrowsingEnabled = enabled; setStringProperty(CATALOG_BROWSING_ENABLED, Boolean.toString(enabled), true); } - - public boolean isCatalogAddAtLast() { - return catalogAddLast; + + public boolean isCatalogMultiSelectEnabled() { + return catalogMultiSelectEnabled; } - - public void setCatalogAddAtLast(boolean addAtLast) { - catalogAddLast = addAtLast; - setStringProperty(CATALOG_ADD_AT_LAST, Boolean.toString(addAtLast), true); + + public void setCatalogMultiSelectEnabled(boolean enabled) { + this.catalogMultiSelectEnabled = enabled; + setStringProperty(CATALOG_MULTI_SELECT_ENABLED, Boolean.toString(enabled), true); + } + + public int getCatalogAddEntryPosition() { + return catalogAddEntryPosition; + } + + public void setCatalogAddEntryPosition(int position) { + catalogAddEntryPosition = position; + setIntProperty(CATALOG_ADD_ENTRY_POSITION, position, true); + } + + public int getCatalogAddCategoryPosition() { + return catalogAddCategoryPosition; + } + + public void setCatalogAddCategoryPosition(int position) { + catalogAddCategoryPosition = position; + setIntProperty(CATALOG_ADD_CATEGORY_POSITION, position, true); } public boolean isMyCoursesSearchEnabled() { @@ -362,7 +392,7 @@ public class RepositoryModule extends AbstractSpringModule { this.lifecycleAutoDelete = lifecycleAutoDelete; setStringProperty(LIFECYCLE_AUTO_DELETE, lifecycleAutoDelete, true); } - + public boolean isLifecycleNotificationByCloseDeleteEnabled() { return "enabled".equals(lifecycleNotificationByCloseDelete); } @@ -380,4 +410,4 @@ public class RepositoryModule extends AbstractSpringModule { this.taxonomyTreeKey = taxonomyTreeKey; setStringProperty(TAXONOMY_TREE_KEY, taxonomyTreeKey, true); } -} \ No newline at end of file +} diff --git a/src/main/java/org/olat/repository/manager/CatalogManager.java b/src/main/java/org/olat/repository/manager/CatalogManager.java index 8182259f533..6d91434c944 100644 --- a/src/main/java/org/olat/repository/manager/CatalogManager.java +++ b/src/main/java/org/olat/repository/manager/CatalogManager.java @@ -26,11 +26,13 @@ package org.olat.repository.manager; import java.io.File; +import java.text.Collator; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; - +import java.util.stream.Collectors; import javax.persistence.FlushModeType; import javax.persistence.TypedQuery; @@ -155,7 +157,37 @@ public class CatalogManager implements UserDataDeletable, InitializingBean { * @return List of catalog entries that are childern entries of given entry */ public List<CatalogEntry> getChildrenOf(CatalogEntry ce) { - return getChildrenOf(ce, 0, -1, CatalogEntry.OrderBy.position, true); + List<CatalogEntry> children = getChildrenOf(ce, 0, -1, CatalogEntry.OrderBy.position, true); + + if (isCategorySortingManually(ce) || isEntrySortingManually(ce)) { + // Create 3 lists: Categories, entries, closed entries + String closed = RepositoryEntryStatusEnum.closed.name(); + List<CatalogEntry> categories = children.stream().filter(catalogEntry -> catalogEntry.getType() == CatalogEntry.TYPE_NODE).collect(Collectors.toList()); + children.removeAll(categories); + List<CatalogEntry> entries = children.stream().filter(catalogEntry -> catalogEntry.getType() == CatalogEntry.TYPE_LEAF && !catalogEntry.getRepositoryEntry().getStatus().equals(closed)).collect(Collectors.toList()); + children.removeAll(entries); + // To be sure only correct entries are in the final list, the last step is also filtered + List<CatalogEntry> closedEntries = children.stream().filter(catalogEntry -> catalogEntry.getType() == CatalogEntry.TYPE_LEAF && catalogEntry.getRepositoryEntry().getStatus().equals(closed)).collect(Collectors.toList()); + // Now remove all remaining entries + children.removeAll(children); + + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.IDENTICAL); + + if (isCategorySortingManually(ce)) { + categories.sort(Comparator.comparing(CatalogEntry::getName, collator)); + } + if (isEntrySortingManually(ce)) { + entries.sort(Comparator.comparing(CatalogEntry::getName, collator)); + closedEntries.sort(Comparator.comparing(CatalogEntry::getName, collator)); + } + + children.addAll(categories); + children.addAll(entries); + children.addAll(closedEntries); + } + + return children; } /** @@ -597,8 +629,6 @@ public class CatalogManager implements UserDataDeletable, InitializingBean { parentEntry = loadCatalogEntry(parentEntry); newEntry = loadCatalogEntry(newEntry); List<CatalogEntry> catEntries = parentEntry.getChildren(); - int index = 0; - boolean added = false; String closed = RepositoryEntryStatusEnum.closed.name(); RepositoryEntry repoEntry = newEntry.getRepositoryEntry(); @@ -608,60 +638,59 @@ public class CatalogManager implements UserDataDeletable, InitializingBean { } cleanNullEntries(catEntries); - - for (CatalogEntry catalogEntry : catEntries) { - // Add entries - if (catalogEntry.getType() == CatalogEntry.TYPE_LEAF && newEntry.getType() == CatalogEntry.TYPE_LEAF) { - if (repositoryModule.isCatalogAddAtLast()) { - // Closed entry to the end - if (repoEntry.getStatus().equals(closed)) { - catEntries.add(newEntry); - added = true; - break; - } - // Not closed entry to the end of not closed entries - else if (catalogEntry.getRepositoryEntry().getStatus().equals(closed)) { - catEntries.add(index, newEntry); - added = true; - break; - } - } else { - // Closed entry to the beginning of closed - if (repoEntry.getStatus().equals(closed) && catalogEntry.getRepositoryEntry().getStatus().equals(closed)) { - catEntries.add(index, newEntry); - added = true; - break; - } - // Not closed entry to the beginning of not closed entries - else if (!repoEntry.getStatus().equals(closed) && !catalogEntry.getRepositoryEntry().getStatus().equals(closed)) { - catEntries.add(index, newEntry); - added = true; - break; - } - } - } - // Add categories - else if (newEntry.getType() == CatalogEntry.TYPE_NODE) { - if (repositoryModule.isCatalogAddAtLast()) { - if (catalogEntry.getType() == CatalogEntry.TYPE_LEAF) { - catEntries.add(index, newEntry); - added = true; - break; - } - } else { - catEntries.add(0, newEntry); - added = true; - break; - } + + // Create 3 lists: Categories, entries, closed entries + List<CatalogEntry> categories = catEntries.stream().filter(catalogEntry -> catalogEntry.getType() == CatalogEntry.TYPE_NODE).collect(Collectors.toList()); + catEntries.removeAll(categories); + List<CatalogEntry> entries = catEntries.stream().filter(catalogEntry -> catalogEntry.getType() == CatalogEntry.TYPE_LEAF && !catalogEntry.getRepositoryEntry().getStatus().equals(closed)).collect(Collectors.toList()); + catEntries.removeAll(entries); + // To be sure only correct entries are in the final list, the last step is also filtered + List<CatalogEntry> closedEntries = catEntries.stream().filter(catalogEntry -> catalogEntry.getType() == CatalogEntry.TYPE_LEAF && catalogEntry.getRepositoryEntry().getStatus().equals(closed)).collect(Collectors.toList()); + // Now remove all remaining entries + catEntries.removeAll(catEntries); + + // Add to categories + if (newEntry.getType() == CatalogEntry.TYPE_NODE) { + // If added on top or alphabetically + if ((parentEntry.getCategoryAddPosition() == null && repositoryModule.getCatalogAddCategoryPosition() == 1) + || (parentEntry.getCategoryAddPosition() != null && parentEntry.getCategoryAddPosition() == 1)) { + categories.add(0, newEntry); + } + // If added in the end + else { + categories.add(newEntry); + } + + } + // Add to entries + else if (newEntry.getType() == CatalogEntry.TYPE_LEAF && !newEntry.getRepositoryEntry().getStatus().equals(closed)) { + // If added on top or alphabetically + if ((parentEntry.getEntryAddPosition() == null && repositoryModule.getCatalogAddEntryPosition() == 1) + || (parentEntry.getEntryAddPosition() != null && parentEntry.getEntryAddPosition() == 1)) { + entries.add(0, newEntry); + } + // If added in the end + else { + entries.add(newEntry); } - index++; } - - // If not added already, add it to the bottom of the list - if (!added) { - catEntries.add(newEntry); + // Add to closed entries + else { + // If added on top or alphabetically + if ((parentEntry.getEntryAddPosition() == null && repositoryModule.getCatalogAddEntryPosition() == 1) + || (parentEntry.getEntryAddPosition() != null && parentEntry.getEntryAddPosition() == 1)) { + closedEntries.add(0, newEntry); + } + // If added in the end + else { + closedEntries.add(newEntry); + } } + catEntries.addAll(categories); + catEntries.addAll(entries); + catEntries.addAll(closedEntries); + updateCatalogEntry(parentEntry); } @@ -985,4 +1014,24 @@ public class CatalogManager implements UserDataDeletable, InitializingBean { return -1; } } + + public void setCategoryAddPosition(CatalogEntry catEntry, Integer position) { + catEntry = loadCatalogEntry(catEntry); + catEntry.setCategoryAddPosition(position); + updateCatalogEntry(catEntry); + } + + public void setEntryAddPosition(CatalogEntry catEntry, Integer position) { + catEntry = loadCatalogEntry(catEntry); + catEntry.setEntryAddPosition(position); + updateCatalogEntry(catEntry); + } + + public boolean isEntrySortingManually(CatalogEntry ce) { + return !((ce.getEntryAddPosition() != null && ce.getEntryAddPosition() == 0) || (ce.getEntryAddPosition() == null && repositoryModule.getCatalogAddEntryPosition() == 0)); + } + + public boolean isCategorySortingManually(CatalogEntry ce) { + return !((ce.getCategoryAddPosition() != null && ce.getCategoryAddPosition() == 0) || (ce.getCategoryAddPosition() == null && repositoryModule.getCatalogAddCategoryPosition() == 0)); + } } diff --git a/src/main/java/org/olat/repository/model/CatalogEntryImpl.java b/src/main/java/org/olat/repository/model/CatalogEntryImpl.java index a3f07ebcfa5..20111f2381a 100644 --- a/src/main/java/org/olat/repository/model/CatalogEntryImpl.java +++ b/src/main/java/org/olat/repository/model/CatalogEntryImpl.java @@ -123,7 +123,13 @@ public class CatalogEntryImpl implements CatalogEntry { @GeneratedValue @Column(name = "order_index", updatable = false, insertable = false) private Integer position; - + + @Column(name = "add_entry_position", unique = false, nullable = true) + private Integer addEntryPosition; + + @Column(name = "add_category_position", unique = false, nullable = true) + private Integer addCategoryPosition; + public CatalogEntryImpl() { // for hibernate @@ -161,6 +167,26 @@ public class CatalogEntryImpl implements CatalogEntry { this.shortTitle = shortTitle; } + @Override + public Integer getEntryAddPosition() { + return addEntryPosition; + } + + @Override + public void setEntryAddPosition(Integer addEntryPosition) { + this.addEntryPosition = addEntryPosition; + } + + @Override + public Integer getCategoryAddPosition() { + return addCategoryPosition; + } + + @Override + public void setCategoryAddPosition(Integer addCategoryPosition) { + this.addCategoryPosition = addCategoryPosition; + } + public String getStyleString() { return styleString; } diff --git a/src/main/java/org/olat/repository/ui/admin/CatalogAdminController.java b/src/main/java/org/olat/repository/ui/admin/CatalogAdminController.java index 5b422a05806..e4874d5c362 100644 --- a/src/main/java/org/olat/repository/ui/admin/CatalogAdminController.java +++ b/src/main/java/org/olat/repository/ui/admin/CatalogAdminController.java @@ -44,12 +44,13 @@ public class CatalogAdminController extends FormBasicController { private MultipleSelectionElement enableEl; private MultipleSelectionElement enableBrowsingEl; private MultipleSelectionElement siteEl; + private MultipleSelectionElement addMultipleEntriesEl; private SingleSelection addEntryPosEl; - - + private SingleSelection addCategoryPosEl; + @Autowired private RepositoryModule repositoryModule; - + /** * @param ureq * @param wControl @@ -68,7 +69,7 @@ public class CatalogAdminController extends FormBasicController { enableEl = uifactory.addCheckboxesHorizontal("catalog.enable", "catalog.enable", formLayout, new String[]{"xx"}, new String[]{""}); enableEl.select("xx", enabled); enableEl.addActionListener(FormEvent.ONCLICK); - + enableBrowsingEl = uifactory.addCheckboxesHorizontal("catalog.browsing", "catalog.browsing", formLayout, new String[]{"xx"}, new String[]{""}); enableBrowsingEl.select("xx", repositoryModule.isCatalogBrowsingEnabled()); enableBrowsingEl.setEnabled(enabled); @@ -78,18 +79,37 @@ public class CatalogAdminController extends FormBasicController { siteEl.select("xx", repositoryModule.isCatalogSiteEnabled()); siteEl.setEnabled(enabled); siteEl.addActionListener(FormEvent.ONCLICK); - - String[] addEntryKeys = {AddEntryPosition.top.name(), AddEntryPosition.bottom.name()}; - String[] addEntryValues = {translate("catalog.addposition." + AddEntryPosition.top.name()), translate("catalog.addposition." + AddEntryPosition.bottom.name())}; - - addEntryPosEl = uifactory.addDropdownSingleselect("catalog.addposition", "catalog.addposition", formLayout, addEntryKeys, addEntryValues); - if (repositoryModule.isCatalogAddAtLast()) { + + addMultipleEntriesEl = uifactory.addCheckboxesHorizontal("catalog.add.multiple.entries", "catalog.add.multiple.entries", formLayout, new String[]{"xx"}, new String[]{""}); + addMultipleEntriesEl.select("xx", repositoryModule.isCatalogMultiSelectEnabled()); + addMultipleEntriesEl.setEnabled(enabled); + addMultipleEntriesEl.addActionListener(FormEvent.ONCLICK); + + String[] addEntryKeys = {AddEntryPosition.alphabetical.name(), AddEntryPosition.top.name(), AddEntryPosition.bottom.name()}; + String[] addEntryValues = {translate("catalog.add.position." + AddEntryPosition.alphabetical.name()), translate("catalog.add.position." + AddEntryPosition.top.name()), translate("catalog.add.position." + AddEntryPosition.bottom.name())}; + + addEntryPosEl = uifactory.addDropdownSingleselect("catalog.add.entry.position", "catalog.add.entry.position", formLayout, addEntryKeys, addEntryValues); + if (repositoryModule.getCatalogAddEntryPosition() == 2) { addEntryPosEl.select(AddEntryPosition.bottom.name(), true); - } else { + } else if (repositoryModule.getCatalogAddEntryPosition() == 1) { addEntryPosEl.select(AddEntryPosition.top.name(), true); + } else { + addEntryPosEl.select(AddEntryPosition.alphabetical.name(), true); } addEntryPosEl.setEnabled(enabled); addEntryPosEl.addActionListener(FormEvent.ONCHANGE); + + addCategoryPosEl = uifactory.addDropdownSingleselect("catalog.add.category.position", "catalog.add.category.position", formLayout, addEntryKeys, addEntryValues); + if (repositoryModule.getCatalogAddCategoryPosition() == 2) { + addCategoryPosEl.select(AddEntryPosition.bottom.name(), true); + } else if (repositoryModule.getCatalogAddCategoryPosition() == 1) { + addCategoryPosEl.select(AddEntryPosition.top.name(), true); + } else { + addCategoryPosEl.select(AddEntryPosition.alphabetical.name(), true); + } + + addCategoryPosEl.setEnabled(enabled); + addCategoryPosEl.addActionListener(FormEvent.ONCHANGE); } @Override @@ -111,10 +131,22 @@ public class CatalogAdminController extends FormBasicController { repositoryModule.setCatalogBrowsingEnabled(enableBrowsingEl.isSelected(0)); } else if (source == addEntryPosEl) { if (addEntryPosEl.getSelectedKey().equals(AddEntryPosition.bottom.name())) { - repositoryModule.setCatalogAddAtLast(true); + repositoryModule.setCatalogAddEntryPosition(2); + } else if (addEntryPosEl.getSelectedKey().equals(AddEntryPosition.top.name())) { + repositoryModule.setCatalogAddEntryPosition(1); } else { - repositoryModule.setCatalogAddAtLast(false); + repositoryModule.setCatalogAddEntryPosition(0); } + } else if (source == addCategoryPosEl) { + if (addCategoryPosEl.getSelectedKey().equals(AddEntryPosition.bottom.name())) { + repositoryModule.setCatalogAddCategoryPosition(2); + } else if (addCategoryPosEl.getSelectedKey().equals(AddEntryPosition.top.name())) { + repositoryModule.setCatalogAddCategoryPosition(1); + } else { + repositoryModule.setCatalogAddCategoryPosition(0); + } + } else if (source == addMultipleEntriesEl) { + repositoryModule.setCatalogMultiSelectEnabled(addMultipleEntriesEl.isSelected(0)); } super.formInnerEvent(ureq, source, event); } @@ -123,9 +155,10 @@ public class CatalogAdminController extends FormBasicController { protected void formOK(UserRequest ureq) { // } - + private enum AddEntryPosition { + alphabetical, top, bottom; } -} \ No newline at end of file +} diff --git a/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_de.properties index 306dd7abc5f..1f57933508b 100644 --- a/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_de.properties @@ -2,9 +2,13 @@ admin.catalog.settings=Katalog settings admin.menu.title=Katalog admin.menu.title.alt=Katalog -catalog.addposition=Neue Eintr\u00e4ge hinzuf\u00fcgen -catalog.addposition.bottom=Am Ende -catalog.addposition.top=Am Anfang +catalog.add.entry.position=Neue Eintr\u00e4ge hinzuf\u00fcgen +catalog.add.category.position=Neue Kategorien hinzuf\u00fcgen +catalog.add.multiple.entries=Mehrere Eintr\u00e4ge zusammen hinzuf\u00fcgen +catalog.add.position.alphabetical=Alphabtisch +catalog.add.position.bottom=Am Ende +catalog.add.position.top=Am Anfang catalog.browsing=Katalog in "Kurse" catalog.enable=Katalog einschalten catalog.site=Katalog in eigener Site +catalog.sort.completely=Katalog komplett alphabetisch sortieren diff --git a/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_en.properties index e05ce147533..f64f464baaa 100644 --- a/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/repository/ui/admin/_i18n/LocalStrings_en.properties @@ -2,9 +2,13 @@ admin.catalog.settings=Catalog settings admin.menu.title=Catalog admin.menu.title.alt=Catalog -catalog.addposition=Add new entries -catalog.addposition.bottom=At the end -catalog.addposition.top=At the beginning +catalog.add.entry.position=Add new entries +catalog.add.category.position=Add new categories +catalog.add.multiple.entries=Add multiple entries at once +catalog.add.position.alphabetical=Alphabetical +catalog.add.position.bottom=At the end +catalog.add.position.top=At the beginning catalog.enable=Enable catalog catalog.browsing=Catalog in "Courses" -catalog.site=Catalog in its own site \ No newline at end of file +catalog.site=Catalog in its own site +catalog.sort.completely=Sort complete catalog alphabetically diff --git a/src/main/java/org/olat/repository/ui/catalog/CatalogEntryEditController.java b/src/main/java/org/olat/repository/ui/catalog/CatalogEntryEditController.java index 2147c3ef5ba..3eb7cc7edae 100644 --- a/src/main/java/org/olat/repository/ui/catalog/CatalogEntryEditController.java +++ b/src/main/java/org/olat/repository/ui/catalog/CatalogEntryEditController.java @@ -26,6 +26,7 @@ package org.olat.repository.ui.catalog; import java.io.File; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -44,6 +45,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.FileElementEvent 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.translator.TranslatorHelper; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.WebappHelper; @@ -54,6 +56,7 @@ import org.olat.core.util.vfs.VFSLeaf; import org.olat.repository.CatalogEntry; import org.olat.repository.CatalogEntry.Style; import org.olat.repository.RepositoryManager; +import org.olat.repository.RepositoryModule; import org.olat.repository.manager.CatalogManager; import org.springframework.beans.factory.annotation.Autowired; @@ -72,7 +75,6 @@ import org.springframework.beans.factory.annotation.Autowired; public class CatalogEntryEditController extends FormBasicController { private static final int picUploadlimitKB = 5024; - private static final Set<String> mimeTypes = new HashSet<>(); static { mimeTypes.add("image/gif"); @@ -84,6 +86,9 @@ public class CatalogEntryEditController extends FormBasicController { private static final String[] styleKeys = new String[]{ Style.tiles.name(), Style.list.name(), Style.compact.name() }; + private static final String[] sortSelectKeys = new String[]{ + "add.default", "add.alphabetically", "add.top", "add.bottom" + }; private TextElement nameEl; private TextElement shortTitleEl; @@ -91,11 +96,16 @@ public class CatalogEntryEditController extends FormBasicController { private RichTextElement descriptionEl; private FileElement fileUpload; + private SingleSelection nodesSortSelect; + private SingleSelection entriesSortSelect; + private CatalogEntry parentEntry; private CatalogEntry catalogEntry; @Autowired private CatalogManager catalogManager; + @Autowired + private RepositoryModule repositoryModule; public CatalogEntryEditController(UserRequest ureq, WindowControl wControl, CatalogEntry entry) { this(ureq, wControl, entry, null); @@ -142,7 +152,16 @@ public class CatalogEntryEditController extends FormBasicController { if(!styleEl.isOneSelected()) { styleEl.select(styleKeys[0], true); } - + + String[] translatedNodeKeys = TranslatorHelper.translateAll(getTranslator(), sortSelectKeys); + String[] translatedEntryKeys = TranslatorHelper.translateAll(getTranslator(), sortSelectKeys); + + translatedNodeKeys[0] += " (" + translate(sortSelectKeys[repositoryModule.getCatalogAddCategoryPosition() + 1]) + ")"; + translatedEntryKeys[0] += " (" + translate(sortSelectKeys[repositoryModule.getCatalogAddEntryPosition() + 1]) + ")"; + + nodesSortSelect = uifactory.addDropdownSingleselect("sort.nodes", formLayout, sortSelectKeys, translatedNodeKeys); + entriesSortSelect = uifactory.addDropdownSingleselect("sort.entries", formLayout, sortSelectKeys, translatedEntryKeys); + VFSLeaf img = catalogEntry == null || catalogEntry.getKey() == null ? null : catalogManager.getImage(catalogEntry); fileUpload = uifactory.addFileElement(getWindowControl(), "entry.pic", "entry.pic", formLayout); fileUpload.setMaxUploadSizeKB(picUploadlimitKB, null, null); @@ -160,6 +179,13 @@ public class CatalogEntryEditController extends FormBasicController { formLayout.add(buttonLayout); uifactory.addFormSubmitButton("submit", buttonLayout); uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl()); + + if (catalogEntry.getCategoryAddPosition() != null) { + nodesSortSelect.select(sortSelectKeys[catalogEntry.getCategoryAddPosition() + 1], true); + } + if (catalogEntry.getEntryAddPosition() != null) { + entriesSortSelect.select(sortSelectKeys[catalogEntry.getEntryAddPosition() + 1], true); + } } public CatalogEntry getEditedCatalogEntry() { @@ -243,6 +269,17 @@ public class CatalogEntryEditController extends FormBasicController { } catalogEntry.setDescription(descriptionEl.getValue()); catalogEntry.setShortTitle(shortTitleEl.getValue()); + + if (!nodesSortSelect.getSelectedKey().equals(sortSelectKeys[0])) { + catalogManager.setCategoryAddPosition(catalogEntry, Arrays.asList(sortSelectKeys).indexOf(nodesSortSelect.getSelectedKey()) - 1); + } else { + catalogManager.setCategoryAddPosition(catalogEntry, null); + } + if (!entriesSortSelect.getSelectedKey().equals(sortSelectKeys[0])) { + catalogManager.setEntryAddPosition(catalogEntry, Arrays.asList(sortSelectKeys).indexOf(entriesSortSelect.getSelectedKey()) - 1); + } else { + catalogManager.setEntryAddPosition(catalogEntry,null); + } if(catalogEntry.getKey() == null) { //a new one diff --git a/src/main/java/org/olat/repository/ui/catalog/CatalogNodeController.java b/src/main/java/org/olat/repository/ui/catalog/CatalogNodeController.java index 066a804a229..5a598d3af30 100644 --- a/src/main/java/org/olat/repository/ui/catalog/CatalogNodeController.java +++ b/src/main/java/org/olat/repository/ui/catalog/CatalogNodeController.java @@ -19,9 +19,12 @@ */ package org.olat.repository.ui.catalog; +import java.text.Collator; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; import org.olat.core.dispatcher.mapper.MapperService; import org.olat.core.dispatcher.mapper.manager.MapperKey; @@ -117,31 +120,43 @@ public class CatalogNodeController extends BasicController implements Activateab } List<CatalogEntry> childCe = catalogManager.getChildrenOf(catalogEntry); + List<CatalogEntry> nodeEntries = childCe.stream().filter(entry -> entry != null && entry.getType() == CatalogEntry.TYPE_NODE).collect(Collectors.toList()); List<String> subCategories = new ArrayList<>(); int count = 0; boolean tiles = catalogEntry.getStyle() == Style.tiles; + + // Sort nodeEntries + if (catalogManager.isCategorySortingManually(catalogEntry)) { + Comparator<CatalogEntry> comparator = Comparator.comparingInt(CatalogEntry::getPosition); + nodeEntries.sort(comparator); + } else { + Collator collator = Collator.getInstance(getLocale()); + collator.setStrength(Collator.IDENTICAL); + + // Sort depending on view type + nodeEntries.sort(Comparator.comparing(tiles ? CatalogEntry::getShortTitle : CatalogEntry::getName, collator)); + } - for (CatalogEntry entry : childCe) { - if(entry != null && entry.getType() == CatalogEntry.TYPE_NODE) { - String cmpId = "cat_" + (++count); - - VFSLeaf img = catalogManager.getImage(entry); - if(img != null) { - String imgId = "image_" + count; - mainVC.contextPut(imgId, img.getName()); - } - mainVC.contextPut("k" + cmpId, entry.getKey()); - - String title = StringHelper.escapeHtml(tiles ? entry.getShortTitle() : entry.getName()); - Link link = LinkFactory.createCustomLink(cmpId, "select_node", cmpId, Link.LINK + Link.NONTRANSLATED, mainVC, this); - link.setCustomDisplayText(title); - link.setIconLeftCSS("o_icon o_icon_catalog_sub"); - link.setUserObject(entry.getKey()); - subCategories.add(Integer.toString(count)); - String titleId = "title_" + count; - mainVC.contextPut(titleId, title); + for (CatalogEntry entry : nodeEntries) { + String cmpId = "cat_" + (++count); + + VFSLeaf img = catalogManager.getImage(entry); + if(img != null) { + String imgId = "image_" + count; + mainVC.contextPut(imgId, img.getName()); } + mainVC.contextPut("k" + cmpId, entry.getKey()); + + String title = StringHelper.escapeHtml(tiles ? entry.getShortTitle() : entry.getName()); + Link link = LinkFactory.createCustomLink(cmpId, "select_node", cmpId, Link.LINK + Link.NONTRANSLATED, mainVC, this); + link.setCustomDisplayText(title); + link.setIconLeftCSS("o_icon o_icon_catalog_sub"); + link.setUserObject(entry.getKey()); + subCategories.add(Integer.toString(count)); + String titleId = "title_" + count; + mainVC.contextPut(titleId, title); } + mainVC.contextPut("subCategories", subCategories); //catalog resources @@ -268,4 +283,4 @@ public class CatalogNodeController extends BasicController implements Activateab Collections.reverse(parentLine); activate(ureq, parentLine, null); } -} \ No newline at end of file +} diff --git a/src/main/java/org/olat/repository/ui/catalog/CatalogNodeManagerController.java b/src/main/java/org/olat/repository/ui/catalog/CatalogNodeManagerController.java index c4175e3a55e..002c5bb1acd 100644 --- a/src/main/java/org/olat/repository/ui/catalog/CatalogNodeManagerController.java +++ b/src/main/java/org/olat/repository/ui/catalog/CatalogNodeManagerController.java @@ -19,6 +19,7 @@ */ package org.olat.repository.ui.catalog; +import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -156,8 +157,11 @@ public class CatalogNodeManagerController extends FormBasicController implements private Link contactLink; private Link addCategoryLink; private Link addResourceLink; - private Link orderLink; - + private Link orderManuallyLink; + + private boolean showCategoryUpDownColumn; + private boolean showEntryUpDownColumn; + private DefaultFlexiColumnModel leafUpColumnModel; private DefaultFlexiColumnModel leafDownColumnModel; private DefaultFlexiColumnModel leafPositionColumnModel; @@ -257,8 +261,20 @@ public class CatalogNodeManagerController extends FormBasicController implements isLocalTreeAdmin = localTreeAdmin || catalogManager.isOwner(catalogEntry, getIdentity()); } + if(catalogEntry.getEntryAddPosition() == null) { + showEntryUpDownColumn = repositoryModule.getCatalogAddEntryPosition() != 0; + } else { + showEntryUpDownColumn = catalogEntry.getEntryAddPosition() != 0; + } + + if(catalogEntry.getCategoryAddPosition() == null) { + showCategoryUpDownColumn = repositoryModule.getCatalogAddCategoryPosition() != 0; + } else { + showCategoryUpDownColumn = catalogEntry.getCategoryAddPosition() != 0; + } + initForm(ureq); - + loadEntryInfos(); loadNodesChildren(); loadResources(ureq); @@ -289,25 +305,25 @@ public class CatalogNodeManagerController extends FormBasicController implements leafColumns = new ArrayList<>(); - FlexiTableColumnModel entriesColumnsModel = getCatalogFlexiTableColumnModel("opened-", !isOrdering); + FlexiTableColumnModel entriesColumnsModel = getCatalogFlexiTableColumnModel("opened-", !isOrdering, showEntryUpDownColumn); entriesModel = new CatalogEntryRowModel(entriesColumnsModel); - entriesEl = uifactory.addTableElement(getWindowControl(), "entries", entriesModel, getTranslator(), formLayout); + entriesEl = uifactory.addTableElement(getWindowControl(), "entries", entriesModel, 20, false, getTranslator(), formLayout); - FlexiTableColumnModel closedEntriesColumnsModel = getCatalogFlexiTableColumnModel("closed-", !isOrdering); + FlexiTableColumnModel closedEntriesColumnsModel = getCatalogFlexiTableColumnModel("closed-", !isOrdering, showEntryUpDownColumn); closedEntriesModel = new CatalogEntryRowModel(closedEntriesColumnsModel); - closedEntriesEl = uifactory.addTableElement(getWindowControl(), "closedEntries", closedEntriesModel, getTranslator(), formLayout); + closedEntriesEl = uifactory.addTableElement(getWindowControl(), "closedEntries", closedEntriesModel, 20, false, getTranslator(), formLayout); - FlexiTableColumnModel nodeEntriesColumnsModel = getNodeFlexiTableColumnModel("nodes-"); + FlexiTableColumnModel nodeEntriesColumnsModel = getNodeFlexiTableColumnModel("nodes-", showCategoryUpDownColumn); nodeEntriesModel = new NodeEntryRowModel(nodeEntriesColumnsModel); nodeEntriesEl = uifactory.addTableElement(getWindowControl(), "nodeEntries", nodeEntriesModel, getTranslator(), formLayout); } - private FlexiTableColumnModel getCatalogFlexiTableColumnModel(String cmdPrefix, boolean sortEnabled) { + private FlexiTableColumnModel getCatalogFlexiTableColumnModel(String cmdPrefix, boolean sortEnabled, boolean showUpDownColumn) { //add the table FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); DefaultFlexiColumnModel columnModel; - if (!sortEnabled) { + if (!sortEnabled && showUpDownColumn) { leafUpColumnModel = new DefaultFlexiColumnModel(true, Cols.up.i18nKey(), Cols.up.ordinal(), CMD_UP, false, null); leafUpColumnModel.setCellRenderer(new BooleanCellRenderer( new StaticFlexiCellRenderer("", CMD_UP, "o_icon o_icon-lg o_icon_move_up"), @@ -408,32 +424,34 @@ public class CatalogNodeManagerController extends FormBasicController implements return columnsModel; } - private FlexiTableColumnModel getNodeFlexiTableColumnModel(String cmdPrefix) { + private FlexiTableColumnModel getNodeFlexiTableColumnModel(String cmdPrefix, boolean showUpDownColumn) { //add the table FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); - - nodeUpColumnModel = new DefaultFlexiColumnModel(true, NodeCols.up.i18nKey(), NodeCols.up.ordinal(), CMD_UP, false, null); - nodeUpColumnModel.setCellRenderer(new BooleanCellRenderer( - new StaticFlexiCellRenderer("", CMD_UP, "o_icon o_icon_fw o_icon-lg o_icon_move_up"), - null)); - nodeUpColumnModel.setIconHeader("o_icon o_icon_fw o_icon-lg o_icon_move_up"); - nodeUpColumnModel.setAlignment(FlexiColumnModel.ALIGNMENT_ICON); - nodeUpColumnModel.setAlwaysVisible(true); - - nodeDownColumnModel = new DefaultFlexiColumnModel(true, NodeCols.down.i18nKey(), NodeCols.down.ordinal(), CMD_DOWN, false, null); - nodeDownColumnModel.setCellRenderer(new BooleanCellRenderer( - new StaticFlexiCellRenderer("", CMD_DOWN, "o_icon o_icon_fw o_icon-lg o_icon_move_down"), - null)); - nodeDownColumnModel.setIconHeader("o_icon o_icon_fw o_icon-lg o_icon_move_down"); - nodeDownColumnModel.setAlignment(FlexiColumnModel.ALIGNMENT_ICON); - nodeDownColumnModel.setAlwaysVisible(true); - - nodePositionColumnModel = new DefaultFlexiColumnModel(true, NodeCols.position.i18nKey(), NodeCols.position.ordinal(), false, null); - - columnsModel.addFlexiColumnModel(nodeUpColumnModel); - columnsModel.addFlexiColumnModel(nodeDownColumnModel); - columnsModel.addFlexiColumnModel(nodePositionColumnModel); + if (showUpDownColumn) { + nodeUpColumnModel = new DefaultFlexiColumnModel(true, NodeCols.up.i18nKey(), NodeCols.up.ordinal(), CMD_UP, false, null); + nodeUpColumnModel.setCellRenderer(new BooleanCellRenderer( + new StaticFlexiCellRenderer("", CMD_UP, "o_icon o_icon_fw o_icon-lg o_icon_move_up"), + null)); + nodeUpColumnModel.setIconHeader("o_icon o_icon_fw o_icon-lg o_icon_move_up"); + nodeUpColumnModel.setAlignment(FlexiColumnModel.ALIGNMENT_ICON); + nodeUpColumnModel.setAlwaysVisible(true); + + + nodeDownColumnModel = new DefaultFlexiColumnModel(true, NodeCols.down.i18nKey(), NodeCols.down.ordinal(), CMD_DOWN, false, null); + nodeDownColumnModel.setCellRenderer(new BooleanCellRenderer( + new StaticFlexiCellRenderer("", CMD_DOWN, "o_icon o_icon_fw o_icon-lg o_icon_move_down"), + null)); + nodeDownColumnModel.setIconHeader("o_icon o_icon_fw o_icon-lg o_icon_move_down"); + nodeDownColumnModel.setAlignment(FlexiColumnModel.ALIGNMENT_ICON); + nodeDownColumnModel.setAlwaysVisible(true); + + nodePositionColumnModel = new DefaultFlexiColumnModel(true, NodeCols.position.i18nKey(), NodeCols.position.ordinal(), false, null); + + columnsModel.addFlexiColumnModel(nodeUpColumnModel); + columnsModel.addFlexiColumnModel(nodeDownColumnModel); + columnsModel.addFlexiColumnModel(nodePositionColumnModel); + } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, NodeCols.key.i18nKey(), NodeCols.key.ordinal(), false, null)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, NodeCols.displayName.i18nKey(), NodeCols.displayName.ordinal(), false, null)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, NodeCols.creationDate.i18nKey(), NodeCols.creationDate.ordinal(), false, null)); @@ -523,13 +541,19 @@ public class CatalogNodeManagerController extends FormBasicController implements items.add(row); } } - - Comparator<CatalogEntryRow> comparator = (row1, row2) -> { - return row1.getPosition().compareTo(row2.getPosition()); - }; - Collections.sort(items, comparator); - Collections.sort(closedItems, comparator); + if (catalogManager.isEntrySortingManually(catalogEntry)) { + Comparator<CatalogEntryRow> comparator = Comparator.comparing(CatalogEntryRow::getPosition); + + items.sort(comparator); + closedItems.sort(comparator); + } else { + Collator collator = Collator.getInstance(getLocale()); + collator.setStrength(Collator.IDENTICAL); + + items.sort(Comparator.comparing(CatalogEntryRow::getDisplayname, collator)); + closedItems.sort(Comparator.comparing(CatalogEntryRow::getDisplayname, collator)); + } entriesModel.setObjects(items); entriesEl.reset(true, true, true); @@ -549,6 +573,20 @@ public class CatalogNodeManagerController extends FormBasicController implements List<NodeEntryRow> nodeEntries = new ArrayList<>(); int count = 0; boolean tiles = catalogEntry.getStyle() == Style.tiles; + + if (catalogManager.isCategorySortingManually(catalogEntry)) { + Comparator<CatalogEntry> comparator = Comparator.comparingInt(CatalogEntry::getPosition); + catalogChildren.sort(comparator); + } else { + Collator collator = Collator.getInstance(getLocale()); + collator.setStrength(Collator.IDENTICAL); + + if (catalogEntry.getStyle().equals(Style.tiles)) { + catalogChildren.sort(Comparator.comparing(entry -> entry.getShortTitle() != null ? entry.getShortTitle() : entry.getName(), collator)); + } else { + catalogChildren.sort(Comparator.comparing(CatalogEntry::getName, collator)); + } + } for (CatalogEntry entry : catalogChildren) { if(entry != null && entry.getType() == CatalogEntry.TYPE_NODE) { @@ -581,12 +619,16 @@ public class CatalogNodeManagerController extends FormBasicController implements } flc.contextPut("subCategories", subCategories); - Comparator<NodeEntryRow> comparator = (row1, row2) -> { - return ((Integer)row1.getPosition()).compareTo(row2.getPosition()); - }; + if (catalogManager.isCategorySortingManually(catalogEntry)) { + Comparator<NodeEntryRow> comparator = Comparator.comparingInt(NodeEntryRow::getPosition); + nodeEntries.sort(comparator); + } else { + Collator collator = Collator.getInstance(getLocale()); + collator.setStrength(Collator.IDENTICAL); + + nodeEntries.sort(Comparator.comparing(NodeEntryRow::getDisplayname, collator)); + } - Collections.sort(nodeEntries, comparator); - nodeEntriesModel.setObjects(nodeEntries); nodeEntriesEl.reset(true, true, true); nodeEntriesEl.setVisible(nodeEntriesModel.getRowCount() > 0); @@ -601,12 +643,12 @@ public class CatalogNodeManagerController extends FormBasicController implements if (canAdministrateCategory || canAddLinks) { if (canAdministrateCategory) { - if (orderLink == null) { - orderLink = LinkFactory.createToolLink("order", translate("tools.order.catalog"), this, "o_icon_order"); - orderLink.setElementCssClass("o_sel_catalog_order_category"); - toolbarPanel.addTool(orderLink, Align.right); + if (orderManuallyLink == null) { + orderManuallyLink = LinkFactory.createToolLink("order", translate("tools.order.catalog"), this, "o_icon_order"); + orderManuallyLink.setElementCssClass("o_sel_catalog_order_category"); + toolbarPanel.addTool(orderManuallyLink, Align.right); } else { - orderLink.setVisible(true); + orderManuallyLink.setVisible(true); } } if (canAdministrateCategory) { @@ -809,9 +851,8 @@ public class CatalogNodeManagerController extends FormBasicController implements doOpenPositionDialog(ureq, link, smallest, biggest); } - } - + super.formInnerEvent(ureq, source, event); } @@ -827,7 +868,7 @@ public class CatalogNodeManagerController extends FormBasicController implements doConfirmDelete(ureq); } else if(moveLink == source) { doMoveCategory(ureq); - } else if (orderLink == source) { + } else if (orderManuallyLink == source) { doActivateOrdering(ureq); } else if(addCategoryLink == source) { doAddCategory(ureq); @@ -857,7 +898,6 @@ public class CatalogNodeManagerController extends FormBasicController implements loadResources(ureq); loadEntryInfos(); } - } super.event(ureq, source, event); } @@ -929,6 +969,10 @@ public class CatalogNodeManagerController extends FormBasicController implements RepositoryEntry selectedEntry = entrySearchCtrl.getSelectedEntry(); doAddResource(ureq, selectedEntry); fireEvent(ureq, Event.CHANGED_EVENT); + } else if(event.getCommand().equals(RepositoryTableModel.TABLE_ACTION_SELECT_ENTRIES)) { + List<RepositoryEntry> selectedEntries = entrySearchCtrl.getSelectedEntries(); + selectedEntries.forEach(entry -> doAddResource(ureq, entry)); + fireEvent(ureq, Event.CHANGED_EVENT); } cmc.deactivate(); cleanUp(); @@ -1100,7 +1144,7 @@ public class CatalogNodeManagerController extends FormBasicController implements catModificationLock = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(lockRes, getIdentity(), LOCK_TOKEN, getWindow()); if (catModificationLock.isSuccess()) { - entrySearchCtrl = new RepositorySearchController(translate("choose"), ureq, getWindowControl(), true, false, new String[0], false, null); + entrySearchCtrl = new RepositorySearchController(translate("choose"), ureq, getWindowControl(), true, repositoryModule.isCatalogMultiSelectEnabled(), new String[0], false, null); listenTo(entrySearchCtrl); // OLAT-Admin has search form if (isAdministrator) { @@ -1290,4 +1334,4 @@ public class CatalogNodeManagerController extends FormBasicController implements String businessPath = "[CatalogAdmin:0][CatalogEntry:" + ref.getKey() + "]"; NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl()); } -} \ No newline at end of file +} diff --git a/src/main/java/org/olat/repository/ui/catalog/_content/node.html b/src/main/java/org/olat/repository/ui/catalog/_content/node.html index 1d504e1ffdd..4b2db8e2f0a 100644 --- a/src/main/java/org/olat/repository/ui/catalog/_content/node.html +++ b/src/main/java/org/olat/repository/ui/catalog/_content/node.html @@ -75,6 +75,9 @@ ## Render node list component #if($isOrdering) $r.render("nodeEntries") + #if($r.available("entries")) + <hr> + #end #end ## Render course list component diff --git a/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_de.properties index 8c6e939492e..77ed6a2275d 100644 --- a/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_de.properties @@ -1,4 +1,9 @@ #Mon Mar 02 09:54:04 CET 2009 +add.default=Standard +add.alphabetically=Automatisch - Alphabetische Sortierung +add.bottom=Manuell - Neue Eintr\u00E4ge am Ende +add.new.entries=Neue Eintr\u00E4ge hinzuf\u00FCgen: +add.top=Manuell - Neue Eintr\u00E4ge am Anfang admin.menu.title=Katalog admin.menu.title.alt=Katalog admin.catalog.settings=Katalog settings @@ -16,7 +21,7 @@ catalog.popup.position.biggest=\u226B catalog.popup.position.save=Speichern catalog.popup.position.smaller=\u003C catalog.popup.position.smallest=\u226A -catalog.position.deactivated=Bitte f\u00FCgen Sie weiter Elemente hinzu um die Sortierung zu verändern! +catalog.position.deactivated=Bitte f\u00FCgen Sie weiter Elemente hinzu um die Sortierung zu ver�ndern! catalog.tree.add.already.exists=Die Lernressource {0} ist bereits in dieser Katalogkategorie vorhanden. catalog.tree.add.intro=W\u00E4hlen Sie eine Kategorie aus, in die Sie die Lernressource {0} verschieben m\u00F6chten. catalog.tree.add.title=Lernressource "{0}" in Katalog hinzuf\u00FCgen @@ -47,6 +52,8 @@ filtered.second=). move=Verschieben no.leaves=Dieser Kategorie sind keine Lernressourcen zugeteilt. repo.nocategories=Diese Lernressource ist noch nicht im Katalog eingebunden. +sort.nodes=Sortierung der Kategorien +sort.entries=Sortierung der Eintr\u00E4ge title.categories=Unterkategorien title.leaves=Lernressourcen tocontent=zum Inhalt @@ -63,7 +70,7 @@ tools.move.catalog.entry=Verschieben tools.move.catalog.entry.failed=Es ist ein Fehler aufgetreten, das Element konnte nicht verschoben werden. tools.move.catalog.entry.success=Der Katalogeintrag "{0}" wurde erfolgreich verschoben. tools.new.catalog.categoryrequest=Verwalter kontaktieren -tools.order.catalog=Manuell ordnen +tools.order.catalog=Ordnen tools.pastestructure=Struktur einf\u00FCgen tools.set.catalog.position=Position anpassen entry.pic=Bild diff --git a/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_en.properties index 7e90e35117d..cbb11acee4f 100644 --- a/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/repository/ui/catalog/_i18n/LocalStrings_en.properties @@ -1,4 +1,8 @@ #Tue Dec 16 09:07:21 CET 2014 +add.default=Default +add.alphabetically=Automatic - Alphabetical order +add.bottom=Manual - New items at last +add.top=Manual - New items on top admin.catalog.settings=Catalog settings admin.menu.title=Catalog admin.menu.title.alt=Catalog @@ -46,6 +50,9 @@ list.compact=Compact list move=Move no.leaves=No learning resources assigned to this category repo.nocategories=This learning resource is not yet added to the catalog +sort.alphabetically=Sort alphabetically +sort.nodes=Sorting of categories +sort.entries=Sorting of entries style=Style tiles=Tiles title.categories=Sub-categories @@ -63,7 +70,7 @@ tools.edit.header=Category tools.move.catalog.entry=Move tools.move.catalog.entry.failed=An error occurred. This element could not be moved. tools.move.catalog.entry.success=Catalog entry "{0}" successfully moved -tools.order.catalog=Manual ordering +tools.order.catalog=Ordering tools.new.catalog.categoryrequest=Contact administrator tools.pastestructure=Insert structure tools.set.catalog.position=Modifiy position diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_15_2_3.java b/src/main/java/org/olat/upgrade/OLATUpgrade_15_2_3.java new file mode 100644 index 00000000000..8733c5ff6b4 --- /dev/null +++ b/src/main/java/org/olat/upgrade/OLATUpgrade_15_2_3.java @@ -0,0 +1,136 @@ +/** + * <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.upgrade; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.Properties; + +import org.apache.logging.log4j.Logger; +import org.olat.core.logging.Tracing; +import org.olat.core.util.WebappHelper; +import org.olat.repository.RepositoryModule; +import org.olat.repository.manager.CatalogManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 2 juin 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class OLATUpgrade_15_2_3 extends OLATUpgrade { + + private static final Logger log = Tracing.createLoggerFor(OLATUpgrade_15_2_3.class); + + private static final String VERSION = "OLAT_15.2.3"; + private static final String MIGRATE_CATALOG_SORTING = "MIGRATE CATALOG SORTING"; + + private static final String CATALOG_ADD_LAST = "catalog.add.last"; + + @Autowired + RepositoryModule repositoryModule; + @Autowired + CatalogManager catalogManager; + + public OLATUpgrade_15_2_3() { + super(); + } + + @Override + public String getVersion() { + return VERSION; + } + + @Override + public boolean doPostSystemInitUpgrade(UpgradeManager upgradeManager) { + UpgradeHistoryData uhd = upgradeManager.getUpgradesHistory(VERSION); + if (uhd == null) { + // has never been called, initialize + uhd = new UpgradeHistoryData(); + } else if (uhd.isInstallationComplete()) { + return false; + } + + boolean allOk = true; + allOk &= migrateCatalogSorting(upgradeManager, uhd); + + uhd.setInstallationComplete(allOk); + upgradeManager.setUpgradesHistory(uhd, VERSION); + if(allOk) { + log.info(Tracing.M_AUDIT, "Finished OLATUpgrade_15_2_2 successfully!"); + } else { + log.info(Tracing.M_AUDIT, "OLATUpgrade_15_2_2 not finished, try to restart OpenOlat!"); + } + return allOk; + } + + private boolean migrateCatalogSorting(UpgradeManager upgradeManager, UpgradeHistoryData uhd) { + boolean allOk = true; + if (!uhd.getBooleanDataValue(MIGRATE_CATALOG_SORTING)) { + String userDataDirectory = WebappHelper.getUserDataRoot(); + File configurationPropertiesFile = Paths.get(userDataDirectory, "system", "configuration", "org.olat.repository.RepositoryModule.properties").toFile(); + if (configurationPropertiesFile.exists()) { + InputStream is = null; + OutputStream fileStream = null; + try { + is = new FileInputStream(configurationPropertiesFile); + Properties configuredProperties = new Properties(); + configuredProperties.load(is); + is.close(); + + String addAtLast = configuredProperties.getProperty(CATALOG_ADD_LAST); + if (addAtLast != null) { + if (addAtLast.equals("true")) { + // Add at last + repositoryModule.setCatalogAddCategoryPosition(2); + repositoryModule.setCatalogAddEntryPosition(2); + } else if (addAtLast.equals("false")) { + // Add at first + repositoryModule.setCatalogAddCategoryPosition(1); + repositoryModule.setCatalogAddEntryPosition(1); + } + } else { + // Add alphabetically + repositoryModule.setCatalogAddCategoryPosition(0); + repositoryModule.setCatalogAddEntryPosition(0); + } + } catch (Exception e) { + log.error("Error when reading / writing user properties config file from path::" + configurationPropertiesFile.getAbsolutePath(), e); + allOk &= false; + } finally { + try { + if (is != null ) is.close(); + } catch (Exception e) { + log.error("Could not close stream from " + configurationPropertiesFile.getAbsolutePath(), e); + allOk &= false; + } + } + } + + uhd.setBooleanDataValue(MIGRATE_CATALOG_SORTING, allOk); + upgradeManager.setUpgradesHistory(uhd, VERSION); + } + return allOk; + } +} diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml index 0eaf7eae050..2b176470c00 100644 --- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml @@ -284,8 +284,16 @@ <constructor-arg index="0" value="OLAT_15.2.1" /> <property name="alterDbStatements" value="alter_15_2_x_to_15_2_1.sql" /> </bean> + <bean id="database_upgrade_15_2_3" class="org.olat.upgrade.DatabaseUpgrade"> + <constructor-arg index="0" value="OLAT_15.2.3" /> + <property name="alterDbStatements" value="alter_15_2_x_to_15_2_3.sql" /> + </bean> + <bean id="database_upgrade_15_3_0" class="org.olat.upgrade.DatabaseUpgrade"> + <constructor-arg index="0" value="OLAT_15.3.0" /> + <property name="alterDbStatements" value="alter_15_2_x_to_15_3_0.sql" /> + </bean> </list> </property> </bean> -</beans> \ No newline at end of file +</beans> diff --git a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml index e8ff361b0ec..93e543551db 100644 --- a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml @@ -64,8 +64,9 @@ <bean id="upgrade_15_pre_6_ae" class="org.olat.upgrade.OLATUpgrade_15_pre_6_ae"/> <bean id="upgrade_15_1_0" class="org.olat.upgrade.OLATUpgrade_15_1_0"/> <bean id="upgrade_15_2_0" class="org.olat.upgrade.OLATUpgrade_15_2_0"/> + <bean id="upgrade_15_2_3" class="org.olat.upgrade.OLATUpgrade_15_2_3"/> </list> </property> </bean> -</beans> \ No newline at end of file +</beans> diff --git a/src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql b/src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql new file mode 100644 index 00000000000..c5c25c2ee6f --- /dev/null +++ b/src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql @@ -0,0 +1,2 @@ +alter table o_catentry add column add_entry_position int; +alter table o_catentry add column add_category_position int; \ No newline at end of file diff --git a/src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql b/src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql new file mode 100644 index 00000000000..3cddf35305f --- /dev/null +++ b/src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql @@ -0,0 +1,2 @@ +alter table o_catentry add add_entry_position number default null; +alter table o_catentry add add_category_position number default null; \ No newline at end of file diff --git a/src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql b/src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql new file mode 100644 index 00000000000..c5c25c2ee6f --- /dev/null +++ b/src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql @@ -0,0 +1,2 @@ +alter table o_catentry add column add_entry_position int; +alter table o_catentry add column add_category_position int; \ No newline at end of file -- GitLab