/** * <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.admin.sysinfo; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.olat.NewControllerFactory; import org.olat.admin.sysinfo.gui.LargeFilesAgeCellRenderer; import org.olat.admin.sysinfo.gui.LargeFilesLockedCellRenderer; import org.olat.admin.sysinfo.gui.LargeFilesNameCellRenderer; import org.olat.admin.sysinfo.gui.LargeFilesRevisionCellRenderer; import org.olat.admin.sysinfo.gui.LargeFilesSendMailCellRenderer; import org.olat.admin.sysinfo.gui.LargeFilesSizeCellRenderer; import org.olat.admin.sysinfo.gui.LargeFilesTrashedCellRenderer; import org.olat.admin.sysinfo.model.LargeFilesTableContentRow; import org.olat.admin.sysinfo.model.LargeFilesTableModel; import org.olat.admin.sysinfo.model.LargeFilesTableModel.LargeFilesTableColumns; import org.olat.core.commons.persistence.SortKey; import org.olat.core.commons.services.vfs.VFSFilterKeys; import org.olat.core.commons.services.vfs.VFSMetadata; import org.olat.core.commons.services.vfs.VFSRepositoryModule; import org.olat.core.commons.services.vfs.VFSRepositoryService; import org.olat.core.commons.services.vfs.VFSRevision; 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.DateChooser; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; 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.FormEvent; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.ExtendedFlexiTableSearchController; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.text.TextFactory; import org.olat.core.gui.components.util.KeyValues; 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.CalloutSettings; import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.util.CSSHelper; import org.olat.core.id.Identity; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.mail.ContactList; import org.olat.core.util.mail.ContactMessage; import org.olat.modules.co.ContactFormController; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 23 Dec 2019<br> * @author aboeckle, alexander.boeckle@frentix.com, http://www.frentix.com * */ public class LargeFilesController extends FormBasicController implements ExtendedFlexiTableSearchController { private final AtomicInteger counter = new AtomicInteger(); private FlexiTableElement largeFilesTableElement; private LargeFilesTableModel largeFilesTableModel; private SingleSelection trashedSelection; private SingleSelection revisionSelection; private SingleSelection lockedSelection; private DateChooser createdAtNewerChooser; private DateChooser createdAtOlderChooser; private DateChooser editedAtNewerChooser; private DateChooser editedAtOlderChooser; private DateChooser lockedAtNewerChooser; private DateChooser lockedAtOlderChooser; private TextElement downloadCountMinEl; private TextElement revisionCountMinEl; private TextElement maxResultEl; private TextElement minSizeEl; private FormLink resetButton; private List<LargeFilesTableContentRow> rows; private CloseableModalController cmc; private ContactFormController contactCtrl; private CloseableCalloutWindowController pathInfoCalloutCtrl; @Autowired private VFSRepositoryService vfsRepositoryService; @Autowired private VFSRepositoryModule vfsRepositoryModule; public LargeFilesController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl, "large_files"); initForm(ureq); updateModel(); } public void updateModel() { rows = new ArrayList<>(); int maxResults = 100; int downloadCountMin = 0; int minSize = 0; Long revisionsCountMin = Long.valueOf(0); if(StringHelper.containsNonWhitespace(maxResultEl.getValue())) { maxResults = Integer.parseInt(maxResultEl.getValue()); } if(StringHelper.containsNonWhitespace(minSizeEl.getValue())) { minSize = (int) (Double.parseDouble(minSizeEl.getValue()) * Formatter.BYTE_UNIT * Formatter.BYTE_UNIT); System.out.println(minSize); } if(StringHelper.containsNonWhitespace(downloadCountMinEl.getValue())) { downloadCountMin = Integer.parseInt(downloadCountMinEl.getValue()); } if(StringHelper.containsNonWhitespace(revisionCountMinEl.getValue())) { revisionsCountMin = Long.parseLong(revisionCountMinEl.getValue()); } if(!revisionSelection.getSelectedKey().equals(VFSFilterKeys.REVISIONS.name())) { List<VFSMetadata> files = vfsRepositoryService.getLargestFiles(maxResults, createdAtNewerChooser.getDate(), createdAtOlderChooser.getDate(), editedAtNewerChooser.getDate(), editedAtOlderChooser.getDate(), lockedAtNewerChooser.getDate(), lockedAtOlderChooser.getDate(), trashedSelection.getSelectedKey(), lockedSelection.getSelectedKey(), downloadCountMin, revisionsCountMin, minSize); for(VFSMetadata file:files) { LargeFilesTableContentRow contentRow = new LargeFilesTableContentRow(file); String[] path = contentRow.getPath().split("/"); StringBuilder sb = new StringBuilder(path[0]); if(path.length > 1) { sb.append("/").append(path[1]); if(path.length > 2) { sb.append("/..."); } } FormLink pathInfo = uifactory.addFormLink("pathinfo_" + counter.incrementAndGet() , "pathInfo", sb.toString(), null, null, Link.NONTRANSLATED); pathInfo.setUserObject(contentRow); contentRow.setPathInfo(pathInfo); rows.add(contentRow); } } if(!revisionSelection.getSelectedKey().equals(VFSFilterKeys.FILES.name())) { List<VFSRevision> revisions = vfsRepositoryService.getLargestRevisions(maxResults, createdAtNewerChooser.getDate(), createdAtOlderChooser.getDate(), editedAtNewerChooser.getDate(), editedAtOlderChooser.getDate(), lockedAtNewerChooser.getDate(), lockedAtOlderChooser.getDate(), trashedSelection.getSelectedKey(), lockedSelection.getSelectedKey(), downloadCountMin, revisionsCountMin, minSize); for(VFSRevision revision:revisions) { LargeFilesTableContentRow contentRow = new LargeFilesTableContentRow(revision); String[] path = contentRow.getPath().split("/"); StringBuilder sb = new StringBuilder(path[0]); if(path.length > 1) { sb.append("/").append(path[1]); if(path.length > 2) { sb.append("/..."); } } FormLink pathInfo = uifactory.addFormLink("pathinfo_" + counter.incrementAndGet() , "pathInfo", sb.toString(), null, null, Link.NONTRANSLATED); pathInfo.setUserObject(contentRow); contentRow.setPathInfo(pathInfo); rows.add(contentRow); } } Collections.sort(rows, (row1,row2) -> { return row2.getSize().intValue() - row1.getSize().intValue(); }); if(maxResults != 0 && maxResults < rows.size()) { rows = rows.subList(0, maxResults); } largeFilesTableModel.setObjects(rows); largeFilesTableElement.reset(true, true, true); } private void resetForm() { createdAtNewerChooser.reset(); createdAtOlderChooser.reset(); lockedAtNewerChooser.reset(); lockedAtOlderChooser.reset(); editedAtNewerChooser.reset(); editedAtOlderChooser.reset(); revisionCountMinEl.reset(); downloadCountMinEl.reset(); trashedSelection.reset(); lockedSelection.reset(); revisionSelection.reset(); maxResultEl.reset(); minSizeEl.reset(); updateModel(); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FormLayoutContainer largefFilesTitle = FormLayoutContainer.createVerticalFormLayout("largeFilesTitle", getTranslator()); formLayout.add(largefFilesTitle); largefFilesTitle.setFormTitle(translate("largefiles.title")); FormLayoutContainer leftContainer = FormLayoutContainer.createDefaultFormLayout_6_6("filter_left", getTranslator()); leftContainer.setRootForm(mainForm); formLayout.add(leftContainer); FormLayoutContainer rightContainer = FormLayoutContainer.createDefaultFormLayout_6_6("filter_right", getTranslator()); leftContainer.setRootForm(mainForm); formLayout.add(rightContainer); FormLayoutContainer filterButtonLayout = FormLayoutContainer.createButtonLayout("filter_buttons", getTranslator()); leftContainer.setRootForm(mainForm); formLayout.add(filterButtonLayout); // Left part of the filter createdAtNewerChooser = uifactory.addDateChooser("largefiles.filter.created.newer", "largefiles.filter.created.newer", null, leftContainer); editedAtNewerChooser = uifactory.addDateChooser("largefiles.filter.edited.newer", "largefiles.filter.edited.newer", null, leftContainer); lockedAtNewerChooser = uifactory.addDateChooser("largefiles.filter.locked.newer", null, leftContainer); revisionCountMinEl = uifactory.addTextElement("largefiles.filter.revision.count.min", 4, "", leftContainer); downloadCountMinEl = uifactory.addTextElement("largefiles.filter.download.count.min", 8, "", leftContainer); maxResultEl = uifactory.addTextElement("largefiles.filter.results.max", 5, "100", leftContainer); minSizeEl = uifactory.addTextElement("largefiles.filter.size.min", 18, "", leftContainer); // Right part of the filter createdAtOlderChooser = uifactory.addDateChooser("largefiles.filter.created.older", "largefiles.filter.created.older", null, rightContainer); editedAtOlderChooser = uifactory.addDateChooser("largefiles.filter.edited.older", "largefiles.filter.edited.older", null, rightContainer); lockedAtOlderChooser = uifactory.addDateChooser("largefiles.filter.locked.older", null, rightContainer); KeyValues deletedKeys = new KeyValues(); deletedKeys.add(KeyValues.entry(VFSFilterKeys.DELETED.name(), translate("largefiles.filter.trashed.only"))); deletedKeys.add(KeyValues.entry(VFSFilterKeys.NOT_DELETED.name(), translate("largefiles.filter.trashed.not"))); deletedKeys.add(KeyValues.entry(VFSFilterKeys.BOTH_DELETED.name(), translate("largefiles.filter.trashed.both"))); trashedSelection = uifactory.addRadiosHorizontal("largefiles.filter.trashed", "largefiles.filter.trashed", rightContainer, deletedKeys.keys(), deletedKeys.values()); trashedSelection.select(VFSFilterKeys.BOTH_DELETED.name(), true); KeyValues revisionKeys = new KeyValues(); revisionKeys.add(KeyValues.entry(VFSFilterKeys.REVISIONS.name(), translate("largefiles.filter.revision.only"))); revisionKeys.add(KeyValues.entry(VFSFilterKeys.FILES.name(), translate("largefiles.filter.revision.not"))); revisionKeys.add(KeyValues.entry(VFSFilterKeys.BOTH_REVISIONS_FILES.name(), translate("largefiles.filter.revision.both"))); revisionSelection = uifactory.addRadiosHorizontal("largefiles.filter.revision", "largefiles.filter.revision", rightContainer, revisionKeys.keys(), revisionKeys.values()); revisionSelection.select(VFSFilterKeys.BOTH_REVISIONS_FILES.name(), true); KeyValues lockedValues = new KeyValues(); lockedValues.add(KeyValues.entry(VFSFilterKeys.LOCKED.name(), translate("largefiles.filter.locked.only"))); lockedValues.add(KeyValues.entry(VFSFilterKeys.NOT_LOCKED.name(), translate("largefiles.filter.locked.not"))); lockedValues.add(KeyValues.entry(VFSFilterKeys.BOTH_LOCKED.name(), translate("largefiles.filter.locked.both"))); lockedSelection = uifactory.addRadiosHorizontal("largefiles.filter.locked", rightContainer, lockedValues.keys(), lockedValues.values()); lockedSelection.select(VFSFilterKeys.BOTH_LOCKED.name(), true); // Filter buttons uifactory.addFormSubmitButton("largefiles.filter.button.search", filterButtonLayout); resetButton = uifactory.addFormLink("largefiles.filter.button.reset", filterButtonLayout, Link.BUTTON); // Tabled FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); DefaultFlexiColumnModel column; columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.key)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.uuid)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, LargeFilesTableColumns.name, new LargeFilesNameCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, LargeFilesTableColumns.size, new LargeFilesSizeCellRenderer(vfsRepositoryModule))); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, LargeFilesTableColumns.path)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.age, new LargeFilesAgeCellRenderer())); column = new DefaultFlexiColumnModel(false, LargeFilesTableColumns.trashed, new LargeFilesTrashedCellRenderer()); column.setIconHeader(CSSHelper.getIconCssClassFor(CSSHelper.CSS_CLASS_TRASHED)); columnsModel.addFlexiColumnModel(column); column = new DefaultFlexiColumnModel(false, LargeFilesTableColumns.revision, new LargeFilesRevisionCellRenderer()); column.setIconHeader(CSSHelper.getIconCssClassFor(CSSHelper.CSS_CLASS_REVISION)); columnsModel.addFlexiColumnModel(column); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.revisionNr)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.revisionComment)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.fileCategory)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.fileType)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.downloadCount)); DefaultFlexiColumnModel sendMail = new DefaultFlexiColumnModel(false, LargeFilesTableColumns.sendMail, "sendMail", new LargeFilesSendMailCellRenderer()); sendMail.setIconHeader(CSSHelper.getIconCssClassFor(CSSHelper.CSS_CLASS_MAIL)); columnsModel.addFlexiColumnModel(sendMail); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.author, "selectAuthor")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.license)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.language)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.source)); column = new DefaultFlexiColumnModel(false, LargeFilesTableColumns.locked, new LargeFilesLockedCellRenderer()); column.setIconHeader(CSSHelper.getIconCssClassFor(CSSHelper.CSS_CLASS_LOCKED)); columnsModel.addFlexiColumnModel(column); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.lockedAt)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.lockedBy, "selectLockedBy")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.creator)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.publisher)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.pubDate)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.createdAt)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, LargeFilesTableColumns.lastModifiedAt)); largeFilesTableModel = new LargeFilesTableModel(columnsModel, getLocale()); largeFilesTableElement = uifactory.addTableElement(getWindowControl(), "large_files", largeFilesTableModel, getTranslator(), formLayout); FlexiTableSortOptions sortOptions = new FlexiTableSortOptions(); sortOptions.setDefaultOrderBy(new SortKey(LargeFilesTableColumns.size.name(), false)); sortOptions.setFromColumnModel(true); largeFilesTableElement.setSortSettings(sortOptions); largeFilesTableElement.setAndLoadPersistedPreferences(ureq, "admin-large-files-list"); largeFilesTableElement.setSearchEnabled(false); largeFilesTableElement.setExportEnabled(true); } @Override protected void doDispose() { } @Override protected void formOK(UserRequest ureq) { updateModel(); } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(source == largeFilesTableElement) { if(event instanceof SelectionEvent) { SelectionEvent te = (SelectionEvent) event; String cmd = te.getCommand(); LargeFilesTableContentRow contentRow = largeFilesTableModel.getObject(te.getIndex()); if("selectAuthor".equals(cmd)) { if (contentRow.getAuthor() != null) { openUser(ureq, contentRow.getAuthor().getKey()); } } else if("selectLockedBy".equals(cmd)) { if (contentRow.getLockedBy() != null) { openUser(ureq, contentRow.getLockedBy().getKey()); } } else if("sendMail".equals(cmd)) { if (contentRow.getAuthor() != null) { contactUser(ureq, contentRow.getAuthor()); } } } } else if(source == resetButton) { resetForm(); } else if(source instanceof FormLink) { FormLink link = (FormLink) source; if("pathInfo".equals(link.getCmd())) { removeAsListenerAndDispose(pathInfoCalloutCtrl); LargeFilesTableContentRow row = (LargeFilesTableContentRow) link.getUserObject(); CalloutSettings settings = new CalloutSettings(false); pathInfoCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), TextFactory.createTextComponentFromString("pathInfo", row.getPath(), "", true, null), link.getFormDispatchId(), "", true, "", settings); listenTo(pathInfoCalloutCtrl); pathInfoCalloutCtrl.activate(); } } super.formInnerEvent(ureq, source, event); } @Override protected void event(UserRequest ureq, Controller source, Event event) { if (source == cmc) { cleanUp(); } else if (source == contactCtrl) { cmc.deactivate(); cleanUp(); } super.event(ureq, source, event); } private void cleanUp() { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(contactCtrl); removeAsListenerAndDispose(pathInfoCalloutCtrl); cmc = null; contactCtrl = null; pathInfoCalloutCtrl = null; } @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOK = super.validateFormLogic(ureq); if(maxResultEl.getValue() != "") { try { if(Integer.parseInt(maxResultEl.getValue()) <= 0) { maxResultEl.setErrorKey("largefiles.filter.error.small", null); allOK &= false; } } catch (Exception e) { maxResultEl.setErrorKey("largefiles.filter.error.letter", null); allOK &= false; } } if(minSizeEl.getValue() != "") { try { if(Double.parseDouble(minSizeEl.getValue()) <= 0) { minSizeEl.setErrorKey("largefiles.filter.error.small", null); allOK &= false; } } catch (Exception e) { minSizeEl.setErrorKey("largefiles.filter.error.letter", null); allOK &= false; } } if(downloadCountMinEl.getValue() != "") { try { if(Integer.parseInt(downloadCountMinEl.getValue()) <= 0) { downloadCountMinEl.setErrorKey("largefiles.filter.error.small", null); allOK &= false; } } catch (Exception e) { downloadCountMinEl.setErrorKey("largefiles.filter.error.letter", null); allOK &= false; } } if(revisionCountMinEl.getValue() != "") { try { if(Integer.parseInt(revisionCountMinEl.getValue()) <= 0) { revisionCountMinEl.setErrorKey("largefiles.filter.error.small", null); allOK &= false; } } catch (Exception e) { revisionCountMinEl.setErrorKey("largefiles.filter.error.letter", null); allOK &= false; } } return allOK; } private void contactUser(UserRequest ureq, Identity user) { removeAsListenerAndDispose(cmc); ContactMessage cmsg = new ContactMessage(getIdentity()); String fullName = user.getUser().getFirstName() + " " + user.getUser().getLastName(); ContactList contactList = new ContactList(fullName); contactList.add(user); cmsg.addEmailTo(contactList); cmsg.setSubject("Too large files in your personal folder"); String bodyStart = translate("largefiles.mail.start", new String[] {user.getUser().getFirstName() + user.getUser().getLastName()}); String bodyFiles = "<ul>"; String bodyEnd = translate("largefiles.mail.end"); for(LargeFilesTableContentRow row:rows) { if (row.getAuthor() == user) { bodyFiles += "<li><b>" + Formatter.formatBytes(row.getSize()) + "</b> - " +row.getName() + "</li>"; } } bodyFiles += "</ul>"; cmsg.setBodyText(bodyStart + bodyFiles + bodyEnd); contactCtrl = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg, null); listenTo(contactCtrl); cmc = new CloseableModalController(getWindowControl(), "close", contactCtrl.getInitialComponent()); cmc.activate(); listenTo(cmc); } @Override public void setEnabled(boolean enable) { // Nothing do to here } @Override public List<String> getConditionalQueries() { return Collections.emptyList(); } private void openUser(UserRequest ureq, Long userKey) { NewControllerFactory.getInstance().launch("[UserAdminSite:0][usearch:0][table:0][Identity:" + userKey.toString() + "]", ureq, getWindowControl()); } }