001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://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: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://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        /** Enable / disable. */
176        activation(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_MACTIVATE_NAME_0,
177        org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_MDEACTIVATE_NAME_0),
178
179        /** Create new job from template. */
180        copy(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_COPY_NAME_0, ""),
181
182        /** Deletes the job. */
183        delete(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_DELETE_NAME_0, ""),
184
185        /** Edits the job. */
186        /** Message constant for key in the resource bundle. */
187        edit(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_EDIT_NAME_0, ""),
188
189        /** Executes the job immediately. */
190        run(org.opencms.workplace.tools.scheduler.Messages.GUI_JOBS_LIST_ACTION_EXECUTE_NAME_0, "");
191
192        /** The message key. */
193        private String m_key;
194
195        /** The message key for activated case.*/
196        private String m_keyActivated;
197
198        /**
199         * Creates a new action.<p>
200         *
201         * @param key the message key for the action
202         * @param activatedKey an (optional) message key
203         */
204        private Action(String key, String activatedKey) {
205
206            m_key = key;
207            m_keyActivated = activatedKey;
208        }
209
210        /**
211         * Returns an activated key.
212         *
213         * @return a message key
214         */
215        String getActivatedMessageKey() {
216
217            return CmsStringUtil.isEmptyOrWhitespaceOnly(m_keyActivated) ? m_key : m_keyActivated;
218        }
219
220        /**
221         * Gets the message key for the action.<p>
222         *
223         * @return the message key
224         */
225        String getMessageKey() {
226
227            return m_key;
228        }
229    }
230
231    /**
232     * The activate job context menu entry.<p>
233     */
234    class ActivateEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
235
236        /**
237         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
238         */
239        public void executeAction(Set<String> data) {
240
241            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
242            CmsScheduledJobInfo jobClone = job.clone();
243            jobClone.setActive(!job.isActive());
244
245            m_manager.writeElement(jobClone);
246            reloadJobs();
247
248        }
249
250        /**
251         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
252         */
253        public String getTitle(Locale locale) {
254
255            return CmsVaadinUtils.getMessageText(Action.activation.getMessageKey());
256        }
257
258        /**
259         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
260         */
261        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
262
263            if ((data == null) || (data.size() > 1) || (m_manager.getElement(data.iterator().next()) == null)) {
264                return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
265            }
266
267            @SuppressWarnings("unchecked")
268            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
269
270            return !job.isActive()
271            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
272            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
273        }
274    }
275
276    /**
277     * The copy job context menu entry.<p>
278     */
279    class CopyEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
280
281        /**
282         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
283         */
284        public void executeAction(Set<String> data) {
285
286            m_manager.openEditDialog(data.iterator().next(), true);
287        }
288
289        /**
290         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
291         */
292        public String getTitle(Locale locale) {
293
294            return CmsVaadinUtils.getMessageText(Action.copy.getMessageKey());
295        }
296
297        /**
298         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
299         */
300        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
301
302            return (data != null) && (data.size() == 1) && (m_manager.getElement(data.iterator().next()) != null)
303            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
304            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
305        }
306    }
307
308    /**
309     * The activate job context menu entry.<p>
310     */
311    class DeActivateEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
312
313        /**
314         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
315         */
316        public void executeAction(Set<String> data) {
317
318            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
319            CmsScheduledJobInfo jobClone = job.clone();
320            jobClone.setActive(!job.isActive());
321
322            m_manager.writeElement(jobClone);
323            reloadJobs();
324
325        }
326
327        /**
328         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
329         */
330        public String getTitle(Locale locale) {
331
332            return CmsVaadinUtils.getMessageText(Action.activation.getActivatedMessageKey());
333        }
334
335        /**
336         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
337         */
338        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
339
340            if ((data == null) || (data.size() > 1) || (m_manager.getElement(data.iterator().next()) == null)) {
341                return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
342            }
343
344            @SuppressWarnings("unchecked")
345            CmsScheduledJobInfo job = (((Set<CmsJobBean>)getValue()).iterator().next()).getJob();
346
347            return job.isActive()
348            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
349            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
350        }
351    }
352
353    /**
354     * The delete job context menu entry.<p>
355     */
356    class DeleteEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
357
358        /**
359         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
360         */
361        @SuppressWarnings("unchecked")
362        public void executeAction(Set<String> data) {
363
364            String jobNames = "";
365            final List<String> jobIds = new ArrayList<String>();
366            List<CmsResourceInfo> jobInfos = new ArrayList<CmsResourceInfo>();
367            for (CmsJobBean job : (Set<CmsJobBean>)getValue()) {
368                jobIds.add(job.getJob().getId());
369                jobNames += job.getName() + ", ";
370                jobInfos.add(getJobInfo(job.getName(), job.getClassName()));
371            }
372            if (!jobNames.isEmpty()) {
373                jobNames = jobNames.substring(0, jobNames.length() - 2);
374            }
375
376            CmsConfirmationDialog.show(
377                CmsVaadinUtils.getMessageText(Action.delete.getMessageKey()),
378                CmsVaadinUtils.getMessageText(Messages.GUI_SCHEDULER_CONFIRM_DELETE_1, jobNames),
379                new Runnable() {
380
381                    public void run() {
382
383                        m_manager.deleteElements(jobIds);
384                        reloadJobs();
385                    }
386                }).displayResourceInfoDirectly(jobInfos);
387        }
388
389        /**
390         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
391         */
392        public String getTitle(Locale locale) {
393
394            return CmsVaadinUtils.getMessageText(Action.delete.getMessageKey());
395        }
396
397        /**
398         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
399         */
400        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
401
402            return (data != null) && (data.size() > 0) && (m_manager.getElement(data.iterator().next()) != null)
403            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
404            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
405        }
406    }
407
408    /**
409     * The edit job context menu entry.<p>
410     */
411    class EditEntry implements I_CmsSimpleContextMenuEntry<Set<String>>, I_CmsSimpleContextMenuEntry.I_HasCssStyles {
412
413        /**
414         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
415         */
416        public void executeAction(Set<String> data) {
417
418            m_manager.openEditDialog(data.iterator().next(), false);
419        }
420
421        /**
422         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry.I_HasCssStyles#getStyles()
423         */
424        public String getStyles() {
425
426            return ValoTheme.LABEL_BOLD;
427        }
428
429        /**
430         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
431         */
432        public String getTitle(Locale locale) {
433
434            return CmsVaadinUtils.getMessageText(Action.edit.getMessageKey());
435        }
436
437        /**
438         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
439         */
440        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
441
442            return (data != null) && (data.size() == 1) && (m_manager.getElement(data.iterator().next()) != null)
443            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
444            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
445        }
446    }
447
448    /**
449     * The delete job context menu entry.<p>
450     */
451    class RunEntry implements I_CmsSimpleContextMenuEntry<Set<String>> {
452
453        /**
454         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object)
455         */
456        public void executeAction(Set<String> data) {
457
458            @SuppressWarnings("unchecked")
459            final CmsScheduledJobInfo job = ((Set<CmsJobBean>)getValue()).iterator().next().getJob();
460
461            CmsConfirmationDialog.show(
462                CmsVaadinUtils.getMessageText(Action.run.getMessageKey()),
463                CmsVaadinUtils.getMessageText(Messages.GUI_SCHEDULER_CONFIRM_EXECUTE_1, job.getJobName()),
464                new Runnable() {
465
466                    public void run() {
467
468                        m_manager.runJob(job);
469                    }
470                }).displayResourceInfoDirectly(
471                    Collections.singletonList(getJobInfo(job.getJobName(), job.getClassName())));
472        }
473
474        /**
475         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale)
476         */
477        public String getTitle(Locale locale) {
478
479            return CmsVaadinUtils.getMessageText(Action.run.getMessageKey());
480        }
481
482        /**
483         * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object)
484         */
485        public CmsMenuItemVisibilityMode getVisibility(Set<String> data) {
486
487            return (data != null) && (data.size() == 1) && (m_manager.getElement(data.iterator().next()) != null)
488            ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE
489            : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
490        }
491    }
492
493    /** Logger instance for this class. */
494    static final Log LOG = CmsLog.getLog(CmsJobTable.class);
495
496    /** Serial version id. */
497    private static final long serialVersionUID = 1L;
498
499    /** The job manager instance. */
500    public CmsJobManagerApp m_manager;
501
502    /** Bean container for the table. */
503    protected BeanItemContainer<CmsJobBean> m_beanContainer;
504
505    /** The context menu. */
506    private CmsContextMenu m_menu;
507
508    /** The available menu entries. */
509    private List<I_CmsSimpleContextMenuEntry<Set<String>>> m_menuEntries;
510
511    /**
512     * Creates a new instance.<p>
513     *
514     * @param manager the job manager instance
515     */
516    public CmsJobTable(CmsJobManagerApp manager) {
517
518        m_manager = manager;
519        m_beanContainer = new BeanItemContainer<CmsJobBean>(CmsJobBean.class);
520        setContainerDataSource(m_beanContainer);
521        setVisibleColumns(
522            TableProperty.className.toString(),
523            TableProperty.name.toString(),
524            TableProperty.lastExecution.toString(),
525            TableProperty.nextExecution.toString());
526        setItemIconPropertyId(TableProperty.icon.toString());
527        setRowHeaderMode(RowHeaderMode.ICON_ONLY);
528        setColumnWidth(null, 40);
529
530        for (TableProperty prop : TableProperty.withHeader()) {
531            setColumnExpandRatio(prop.toString(), 1);
532            setColumnHeader(prop.toString(), CmsVaadinUtils.getMessageText(prop.getMessageKey()));
533        }
534        setSortContainerPropertyId(TableProperty.name.toString());
535        getVisibleColumns();
536        setSelectable(true);
537        setMultiSelect(true);
538        addItemClickListener(new ItemClickListener() {
539
540            private static final long serialVersionUID = -4738296706762013443L;
541
542            public void itemClick(ItemClickEvent event) {
543
544                onItemClick(event, event.getItemId(), event.getPropertyId());
545            }
546        });
547
548        m_menu = new CmsContextMenu();
549        m_menu.setAsTableContextMenu(this);
550
551        setCellStyleGenerator(new CellStyleGenerator() {
552
553            private static final long serialVersionUID = 1L;
554
555            public String getStyle(Table source, Object itemId, Object propertyId) {
556
557                if (TableProperty.className.toString().equals(propertyId)) {
558                    return " " + OpenCmsTheme.HOVER_COLUMN;
559                }
560                @SuppressWarnings("unchecked")
561                CmsScheduledJobInfo job = ((BeanItem<CmsJobBean>)source.getItem(itemId)).getBean().getJob();
562                if (TableProperty.name.toString().equals(propertyId) & job.isActive()) {
563                    return " " + OpenCmsTheme.IN_NAVIGATION;
564                }
565                return null;
566            }
567        });
568
569    }
570
571    /**
572     * Returns the resource info box to the given job.<p>
573     *
574     * @param name the job name
575     * @param className the job class
576     *
577     * @return the info box component
578     */
579    public static CmsResourceInfo getJobInfo(String name, String className) {
580
581        return new CmsResourceInfo(name, className, new CmsCssIcon(OpenCmsTheme.ICON_JOB));
582    }
583
584    /**
585     * Returns the available menu entries.<p>
586     *
587     * @return the menu entries
588     */
589    public List<I_CmsSimpleContextMenuEntry<Set<String>>> getMenuEntries() {
590
591        if (m_menuEntries == null) {
592            m_menuEntries = new ArrayList<I_CmsSimpleContextMenuEntry<Set<String>>>();
593            m_menuEntries.add(new EditEntry());
594            m_menuEntries.add(new ActivateEntry());
595            m_menuEntries.add(new DeActivateEntry());
596            m_menuEntries.add(new CopyEntry());
597            m_menuEntries.add(new DeleteEntry());
598            m_menuEntries.add(new RunEntry());
599        }
600        return m_menuEntries;
601    }
602
603    /**
604     * Reloads the job table data.<p>
605     */
606    public void reloadJobs() {
607
608        m_beanContainer.removeAllItems();
609        for (CmsScheduledJobInfo job : m_manager.getAllElements()) {
610            m_beanContainer.addBean(new CmsJobBean(job));
611        }
612        sort();
613        refreshRowCache();
614
615    }
616
617    /**
618     * Sets the menu entries.<p>
619     *
620     * @param newEntries to be set
621     */
622    public void setMenuEntries(List<I_CmsSimpleContextMenuEntry<Set<String>>> newEntries) {
623
624        m_menuEntries = newEntries;
625    }
626
627    /**
628     * Calls the edit formular to edit a job.<p>
629     *
630     * @param jobId to be edited.
631     */
632    void editJob(String jobId) {
633
634        String stateEdit = CmsJobManagerApp.PATH_NAME_EDIT;
635        stateEdit = A_CmsWorkplaceApp.addParamToState(stateEdit, CmsJobManagerApp.PARAM_JOB_ID, jobId);
636        CmsAppWorkplaceUi.get().showApp(CmsScheduledJobsAppConfig.APP_ID, stateEdit);
637    }
638
639    /**
640     * Handles the table item clicks, including clicks on images inside of a table item.<p>
641     *
642     * @param event the click event
643     * @param itemId of the clicked row
644     * @param propertyId column id
645     */
646    @SuppressWarnings("unchecked")
647    void onItemClick(MouseEvents.ClickEvent event, Object itemId, Object propertyId) {
648
649        if (!event.isCtrlKey() && !event.isShiftKey()) {
650            changeValueIfNotMultiSelect(itemId);
651            // don't interfere with multi-selection using control key
652            if (event.getButton().equals(MouseButton.RIGHT) || (propertyId == null)) {
653                Set<String> jobIds = new HashSet<String>();
654                for (CmsJobBean job : (Set<CmsJobBean>)getValue()) {
655                    jobIds.add(job.getJob().getId());
656                }
657                m_menu.setEntries(getMenuEntries(), jobIds);
658                m_menu.openForTable(event, itemId, propertyId, this);
659            } else if (event.getButton().equals(MouseButton.LEFT)
660                && TableProperty.className.toString().equals(propertyId)) {
661
662                String jobId = ((Set<CmsJobBean>)getValue()).iterator().next().getJob().getId();
663                m_manager.defaultAction(jobId);
664            }
665        }
666    }
667
668    /**
669     * Checks value of table and sets it new if needed:<p>
670     * if multiselect: new itemId is in current Value? -> no change of value<p>
671     * no multiselect and multiselect, but new item not selected before: set value to new item<p>
672     *
673     * @param itemId if of clicked item
674     */
675    private void changeValueIfNotMultiSelect(Object itemId) {
676
677        @SuppressWarnings("unchecked")
678        Set<String> value = (Set<String>)getValue();
679        if (value == null) {
680            select(itemId);
681        } else if (!value.contains(itemId)) {
682            setValue(null);
683            select(itemId);
684        }
685    }
686
687}