001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.apps.scheduler;
029
030import org.opencms.main.CmsLog;
031import org.opencms.scheduler.CmsScheduledJobInfo;
032import org.opencms.ui.CmsCssIcon;
033import org.opencms.ui.CmsVaadinUtils;
034import org.opencms.ui.Messages;
035import org.opencms.ui.apps.A_CmsWorkplaceApp;
036import org.opencms.ui.apps.CmsAppWorkplaceUi;
037import org.opencms.ui.components.CmsConfirmationDialog;
038import org.opencms.ui.components.CmsResourceInfo;
039import org.opencms.ui.components.OpenCmsTheme;
040import org.opencms.ui.contextmenu.CmsContextMenu;
041import org.opencms.ui.contextmenu.CmsMenuItemVisibilityMode;
042import org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry;
043import org.opencms.util.CmsStringUtil;
044
045import java.util.ArrayList;
046import java.util.Collections;
047import java.util.HashSet;
048import java.util.List;
049import java.util.Locale;
050import java.util.Set;
051
052import org.apache.commons.logging.Log;
053
054import com.vaadin.event.MouseEvents;
055import com.vaadin.shared.MouseEventDetails.MouseButton;
056import com.vaadin.ui.themes.ValoTheme;
057import com.vaadin.v7.data.util.BeanItem;
058import com.vaadin.v7.data.util.BeanItemContainer;
059import com.vaadin.v7.event.ItemClickEvent;
060import com.vaadin.v7.event.ItemClickEvent.ItemClickListener;
061import com.vaadin.v7.ui.Table;
062
063/**
064 * Table used to display scheduled jobs, together with buttons for modifying the jobs.<p>
065 * The columns containing the buttons are implemented as generated columns.
066 */
067public class CmsJobTable extends Table {
068
069    /**
070     * Property columns of table, including their Messages for header.<p>
071     */
072    protected enum TableProperty {
073
074        /**
075         * Class name column.
076         */
077        className("className", org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_COL_CLASS_0),
078
079        /**
080         * icon column.
081         */
082        icon("icon", null),
083
084        /**
085         * last execution date column.
086         */
087        lastExecution("lastExecution", org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_COL_LASTEXE_0),
088
089        /**
090         * Name column.
091         */
092        name("name", org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_COL_NAME_0),
093
094        /**
095         * next execution date column.
096         */
097        nextExecution("nextExecution", org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_COL_NEXTEXE_0);
098
099        /**Message for the header.*/
100        private String m_header;
101
102        /**Name of column.*/
103        private String m_name;
104
105        /**
106         * private constructor.<p>
107         *
108         * @param propName name of property
109         * @param header message
110         */
111        private TableProperty(String propName, String header) {
112
113            m_header = header;
114            m_name = propName;
115        }
116
117        /**
118         * returns property from it's name used for table column ids.<p>
119         *
120         * @param propName to looked up
121         * @return the TableProperty
122         */
123        static TableProperty get(String propName) {
124
125            for (TableProperty prop : TableProperty.values()) {
126                if (prop.toString().equals(propName)) {
127                    return prop;
128                }
129            }
130            return null;
131        }
132
133        /**
134         * Returns all columns with header.<p>
135         *
136         * @return set of TableProperty
137         */
138        static Set<TableProperty> withHeader() {
139
140            Set<TableProperty> ret = new HashSet<TableProperty>();
141
142            for (TableProperty prop : TableProperty.values()) {
143                if (prop.getMessageKey() != null) {
144                    ret.add(prop);
145                }
146            }
147            return ret;
148        }
149
150        /**
151         * @see java.lang.Enum#toString()
152         */
153        @Override
154        public String toString() {
155
156            return m_name;
157        }
158
159        /**
160         * Returns the message key.<p>
161         *
162         * @return message
163         */
164        String getMessageKey() {
165
166            return m_header;
167        }
168
169    }
170
171    /**
172     * Enum representing the actions for which buttons exist in the table rows.<p>
173     */
174    enum Action {
175
176        /** Enable / disable. */
177        activation(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_MACTIVATE_NAME_0,
178        org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_MDEACTIVATE_NAME_0),
179
180        /** Create new job from template. */
181        copy(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_COPY_NAME_0, ""),
182
183        /** Deletes the job. */
184        delete(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_DELETE_NAME_0, ""),
185
186        /** Edits the job. */
187        /** Message constant for key in the resource bundle. */
188        edit(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_EDIT_NAME_0, ""),
189
190        /** Executes the job immediately. */
191        run(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_EXECUTE_NAME_0, "");
192
193        /** The message key. */
194        private String m_key;
195
196        /** The message key for activated case.*/
197        private String m_keyActivated;
198
199        /**
200         * Creates a new action.<p>
201         *
202         * @param key the message key for the action
203         * @param activatedKey an (optional) message key
204         */
205        private Action(String key, String activatedKey) {
206
207            m_key = key;
208            m_keyActivated = activatedKey;
209        }
210
211        /**
212         * Returns an activated key.
213         *
214         * @return a message key
215         */
216        String getActivatedMessageKey() {
217
218            return CmsStringUtil.isEmptyOrWhitespaceOnly(m_keyActivated) ? m_key : m_keyActivated;
219        }
220
221        /**
222         * Gets the message key for the action.<p>
223         *
224         * @return the message key
225         */
226        String getMessageKey() {
227
228            return m_key;
229        }
230    }
231
232    /**
233     * The activate job context menu entry.<p>
234     */
235    class ActivateEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
236
237        /**
238         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
239         */
240        public void executeAction(Set<String> data) {
241
242            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
243            CmsScheduledJobInfo jobClone = job.clone();
244            jobClone.setActive(!job.isActive());
245
246            m_manager.writeElement(jobClone);
247            reloadJobs();
248
249        }
250
251        /**
252         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
253         */
254        public String getTitle(Locale locale) {
255
256            return CmsVaadinUtils.getMessageText(Action.activation.getMessageKey());
257        }
258
259        /**
260         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
261         */
262        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
263
264            if ((data == null) || (data.size() > 1) || (m_manager.getElement(data.iterator().next()) == null)) {
265                return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
266            }
267
268            @SuppressWarnings("unchecked")
269            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
270
271            return !job.isActive()
272            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
273            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
274        }
275    }
276
277    /**
278     * The copy job context menu entry.<p>
279     */
280    class CopyEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
281
282        /**
283         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
284         */
285        public void executeAction(Set<String> data) {
286
287            m_manager.openEditDialog(data.iterator().next(), true);
288        }
289
290        /**
291         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
292         */
293        public String getTitle(Locale locale) {
294
295            return CmsVaadinUtils.getMessageText(Action.copy.getMessageKey());
296        }
297
298        /**
299         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
300         */
301        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
302
303            return (data != null) && (data.size() == 1) && (m_manager.getElement(data.iterator().next()) != null)
304            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
305            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
306        }
307    }
308
309    /**
310     * The activate job context menu entry.<p>
311     */
312    class DeActivateEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
313
314        /**
315         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
316         */
317        public void executeAction(Set<String> data) {
318
319            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
320            CmsScheduledJobInfo jobClone = job.clone();
321            jobClone.setActive(!job.isActive());
322
323            m_manager.writeElement(jobClone);
324            reloadJobs();
325
326        }
327
328        /**
329         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
330         */
331        public String getTitle(Locale locale) {
332
333            return CmsVaadinUtils.getMessageText(Action.activation.getActivatedMessageKey());
334        }
335
336        /**
337         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
338         */
339        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
340
341            if ((data == null) || (data.size() > 1) || (m_manager.getElement(data.iterator().next()) == null)) {
342                return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
343            }
344
345            @SuppressWarnings("unchecked")
346            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
347
348            return job.isActive()
349            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
350            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
351        }
352    }
353
354    /**
355     * The delete job context menu entry.<p>
356     */
357    class DeleteEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
358
359        /**
360         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
361         */
362        @SuppressWarnings("unchecked")
363        public void executeAction(Set<String> data) {
364
365            String jobNames = "";
366            final List<String> jobIds = new ArrayList<String>();
367            List<CmsResourceInfo> jobInfos = new ArrayList<CmsResourceInfo>();
368            for (CmsJobBean job : (Set<CmsJobBean>)getValue()) {
369                jobIds.add(job.getJob().getId());
370                jobNames += job.getName() + ", ";
371                jobInfos.add(getJobInfo(job.getName(), job.getClassName()));
372            }
373            if (!jobNames.isEmpty()) {
374                jobNames = jobNames.substring(0, jobNames.length() - 2);
375            }
376
377            CmsConfirmationDialog.show(
378                CmsVaadinUtils.getMessageText(Action.delete.getMessageKey()),
379                CmsVaadinUtils.getMessageText(Messages.GUI_SCHEDULER_CONFIRM_DELETE_1, jobNames),
380                new Runnable() {
381
382                    public void run() {
383
384                        m_manager.deleteElements(jobIds);
385                        reloadJobs();
386                    }
387                }).displayResourceInfoDirectly(jobInfos);
388        }
389
390        /**
391         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
392         */
393        public String getTitle(Locale locale) {
394
395            return CmsVaadinUtils.getMessageText(Action.delete.getMessageKey());
396        }
397
398        /**
399         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
400         */
401        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
402
403            return (data != null) && (data.size() > 0) && (m_manager.getElement(data.iterator().next()) != null)
404            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
405            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
406        }
407    }
408
409    /**
410     * The edit job context menu entry.<p>
411     */
412    class EditEntry implements I_CmsSimpleContextMenuEntry<Set<String>>, I_CmsSimpleContextMenuEntry.I_HasCssStyles {
413
414        /**
415         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
416         */
417        public void executeAction(Set<String> data) {
418
419            m_manager.openEditDialog(data.iterator().next(), false);
420        }
421
422        /**
423         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry.I_HasCssStyles#getStyles()
424         */
425        public String getStyles() {
426
427            return ValoTheme.LABEL_BOLD;
428        }
429
430        /**
431         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
432         */
433        public String getTitle(Locale locale) {
434
435            return CmsVaadinUtils.getMessageText(Action.edit.getMessageKey());
436        }
437
438        /**
439         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
440         */
441        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
442
443            return (data != null) && (data.size() == 1) && (m_manager.getElement(data.iterator().next()) != null)
444            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
445            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
446        }
447    }
448
449    /**
450     * The delete job context menu entry.<p>
451     */
452    class RunEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
453
454        /**
455         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
456         */
457        public void executeAction(Set<String> data) {
458
459            @SuppressWarnings("unchecked")
460            final CmsScheduledJobInfo job = ((Set<CmsJobBean>)getValue()).iterator().next().getJob();
461
462            CmsConfirmationDialog.show(
463                CmsVaadinUtils.getMessageText(Action.run.getMessageKey()),
464                CmsVaadinUtils.getMessageText(Messages.GUI_SCHEDULER_CONFIRM_EXECUTE_1, job.getJobName()),
465                new Runnable() {
466
467                    public void run() {
468
469                        m_manager.runJob(job);
470                    }
471                }).displayResourceInfoDirectly(
472                    Collections.singletonList(getJobInfo(job.getJobName(), job.getClassName())));
473        }
474
475        /**
476         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
477         */
478        public String getTitle(Locale locale) {
479
480            return CmsVaadinUtils.getMessageText(Action.run.getMessageKey());
481        }
482
483        /**
484         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
485         */
486        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
487
488            return (data != null) && (data.size() == 1) && (m_manager.getElement(data.iterator().next()) != null)
489            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
490            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
491        }
492    }
493
494    /** Logger instance for this class. */
495    static final Log LOG = CmsLog.getLog(CmsJobTable.class);
496
497    /** Serial version id. */
498    private static final long serialVersionUID = 1L;
499
500    /** The job manager instance. */
501    public CmsJobManagerApp m_manager;
502
503    /** Bean container for the table. */
504    protected BeanItemContainer<CmsJobBean> m_beanContainer;
505
506    /** The context menu. */
507    private CmsContextMenu m_menu;
508
509    /** The available menu entries. */
510    private List<I_CmsSimpleContextMenuEntry<Set<String>>> m_menuEntries;
511
512    /**
513     * Creates a new instance.<p>
514     *
515     * @param manager the job manager instance
516     */
517    public CmsJobTable(CmsJobManagerApp manager) {
518
519        m_manager = manager;
520        m_beanContainer = new BeanItemContainer<CmsJobBean>(CmsJobBean.class);
521        setContainerDataSource(m_beanContainer);
522        setVisibleColumns(
523            TableProperty.className.toString(),
524            TableProperty.name.toString(),
525            TableProperty.lastExecution.toString(),
526            TableProperty.nextExecution.toString());
527        setItemIconPropertyId(TableProperty.icon.toString());
528        setRowHeaderMode(RowHeaderMode.ICON_ONLY);
529        setColumnWidth(null, 40);
530
531        for (TableProperty prop : TableProperty.withHeader()) {
532            setColumnExpandRatio(prop.toString(), 1);
533            setColumnHeader(prop.toString(), CmsVaadinUtils.getMessageText(prop.getMessageKey()));
534        }
535        setSortContainerPropertyId(TableProperty.name.toString());
536        getVisibleColumns();
537        setSelectable(true);
538        setMultiSelect(true);
539        addItemClickListener(new ItemClickListener() {
540
541            private static final long serialVersionUID = -4738296706762013443L;
542
543            public void itemClick(ItemClickEvent event) {
544
545                onItemClick(event, event.getItemId(), event.getPropertyId());
546            }
547        });
548
549        m_menu = new CmsContextMenu();
550        m_menu.setAsTableContextMenu(this);
551
552        setCellStyleGenerator(new CellStyleGenerator() {
553
554            private static final long serialVersionUID = 1L;
555
556            public String getStyle(Table source, Object itemId, Object propertyId) {
557
558                if (TableProperty.className.toString().equals(propertyId)) {
559                    return " " + OpenCmsTheme.HOVER_COLUMN;
560                }
561                @SuppressWarnings("unchecked")
562                CmsScheduledJobInfo job = ((BeanItem<CmsJobBean>)source.getItem(itemId)).getBean().getJob();
563                if (TableProperty.name.toString().equals(propertyId) & job.isActive()) {
564                    return " " + OpenCmsTheme.IN_NAVIGATION;
565                }
566                return null;
567            }
568        });
569
570    }
571
572    /**
573     * Returns the resource info box to the given job.<p>
574     *
575     * @param name the job name
576     * @param className the job class
577     *
578     * @return the info box component
579     */
580    public static CmsResourceInfo getJobInfo(String name, String className) {
581
582        return new CmsResourceInfo(name, className, new CmsCssIcon(OpenCmsTheme.ICON_JOB));
583    }
584
585    /**
586     * Returns the available menu entries.<p>
587     *
588     * @return the menu entries
589     */
590    public List<I_CmsSimpleContextMenuEntry<Set<String>>> getMenuEntries() {
591
592        if (m_menuEntries == null) {
593            m_menuEntries = new ArrayList<I_CmsSimpleContextMenuEntry<Set<String>>>();
594            m_menuEntries.add(new EditEntry());
595            m_menuEntries.add(new ActivateEntry());
596            m_menuEntries.add(new DeActivateEntry());
597            m_menuEntries.add(new CopyEntry());
598            m_menuEntries.add(new DeleteEntry());
599            m_menuEntries.add(new RunEntry());
600        }
601        return m_menuEntries;
602    }
603
604    /**
605     * Reloads the job table data.<p>
606     */
607    public void reloadJobs() {
608
609        m_beanContainer.removeAllItems();
610        for (CmsScheduledJobInfo job : m_manager.getAllElements()) {
611            m_beanContainer.addBean(new CmsJobBean(job));
612        }
613        sort();
614        refreshRowCache();
615
616    }
617
618    /**
619     * Sets the menu entries.<p>
620     *
621     * @param newEntries to be set
622     */
623    public void setMenuEntries(List<I_CmsSimpleContextMenuEntry<Set<String>>> newEntries) {
624
625        m_menuEntries = newEntries;
626    }
627
628    /**
629     * Calls the edit formular to edit a job.<p>
630     *
631     * @param jobId to be edited.
632     */
633    void editJob(String jobId) {
634
635        String stateEdit = CmsJobManagerApp.PATH_NAME_EDIT;
636        stateEdit = A_CmsWorkplaceApp.addParamToState(stateEdit, CmsJobManagerApp.PARAM_JOB_ID, jobId);
637        CmsAppWorkplaceUi.get().showApp(CmsScheduledJobsAppConfig.APP_ID, stateEdit);
638    }
639
640    /**
641     * Handles the table item clicks, including clicks on images inside of a table item.<p>
642     *
643     * @param event the click event
644     * @param itemId of the clicked row
645     * @param propertyId column id
646     */
647    @SuppressWarnings("unchecked")
648    void onItemClick(MouseEvents.ClickEvent event, Object itemId, Object propertyId) {
649
650        if (!event.isCtrlKey() && !event.isShiftKey()) {
651            changeValueIfNotMultiSelect(itemId);
652            // don't interfere with multi-selection using control key
653            if (event.getButton().equals(MouseButton.RIGHT) || (propertyId == null)) {
654                Set<String> jobIds = new HashSet<String>();
655                for (CmsJobBean job : (Set<CmsJobBean>)getValue()) {
656                    jobIds.add(job.getJob().getId());
657                }
658                m_menu.setEntries(getMenuEntries(), jobIds);
659                m_menu.openForTable(event, itemId, propertyId, this);
660            } else if (event.getButton().equals(MouseButton.LEFT)
661                && TableProperty.className.toString().equals(propertyId)) {
662
663                String jobId = ((Set<CmsJobBean>)getValue()).iterator().next().getJob().getId();
664                m_manager.defaultAction(jobId);
665            }
666        }
667    }
668
669    /**
670     * Checks value of table and sets it new if needed:<p>
671     * if multiselect: new itemId is in current Value? -> no change of value<p>
672     * no multiselect and multiselect, but new item not selected before: set value to new item<p>
673     *
674     * @param itemId if of clicked item
675     */
676    private void changeValueIfNotMultiSelect(Object itemId) {
677
678        @SuppressWarnings("unchecked")
679        Set<String> value = (Set<String>)getValue();
680        if (value == null) {
681            select(itemId);
682        } else if (!value.contains(itemId)) {
683            setValue(null);
684            select(itemId);
685        }
686    }
687
688}