001/* 002 * (C) Copyright 2006-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 * $Id: ActionRegistry.java 20637 2007-06-17 12:37:03Z sfermigier $ 020 */ 021 022package org.nuxeo.ecm.platform.actions; 023 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032 033import org.apache.commons.lang3.StringUtils; 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036 037/** 038 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 039 */ 040public class ActionRegistry implements Serializable { 041 042 private static final Log log = LogFactory.getLog(ActionRegistry.class); 043 044 private static final long serialVersionUID = 8425627293154848041L; 045 046 private final Map<String, Action> actions; 047 048 private final Map<String, List<String>> categories; 049 050 private List<TypeCompatibility> typeCategoryRelations; 051 052 public ActionRegistry() { 053 actions = new HashMap<>(); 054 categories = new HashMap<>(); 055 typeCategoryRelations = new ArrayList<>(); 056 } 057 058 public synchronized void addAction(Action action) { 059 String id = action.getId(); 060 if (log.isDebugEnabled()) { 061 if (actions.containsKey(id)) { 062 log.debug("Overriding action: " + action); 063 } else { 064 log.debug("Registering action: " + action); 065 } 066 } 067 // add a default label if not set 068 if (action.getLabel() == null) { 069 action.setLabel(action.getId()); 070 } 071 actions.put(id, action); 072 for (String category : action.getCategories()) { 073 List<String> acts = categories.get(category); 074 if (acts == null) { 075 acts = new ArrayList<>(); 076 } 077 if (!acts.contains(id)) { 078 acts.add(id); 079 } 080 categories.put(category, acts); 081 } 082 } 083 084 public synchronized Action removeAction(String id) { 085 if (log.isDebugEnabled()) { 086 log.debug("Unregistering action: " + id); 087 } 088 089 Action action = actions.remove(id); 090 if (action != null) { 091 for (String category : action.getCategories()) { 092 List<String> acts = categories.get(category); 093 if (acts != null) { 094 acts.remove(id); 095 } 096 } 097 } 098 return action; 099 } 100 101 public synchronized Collection<Action> getActions() { 102 return Collections.unmodifiableCollection(sortActions(actions.values())); 103 } 104 105 public List<Action> getActions(String category) { 106 List<Action> result = new LinkedList<>(); 107 Collection<String> ids; 108 synchronized (this) { 109 ids = categories.get(category); 110 } 111 if (ids != null) { 112 for (String id : ids) { 113 Action action = actions.get(id); 114 if (action != null && action.isEnabled()) { 115 // UI type action compat check 116 Action finalAction = getClonedAction(action); 117 applyCompatibility(category, finalAction); 118 // return only enabled actions 119 result.add(finalAction); 120 } 121 } 122 } 123 result = sortActions(result); 124 return result; 125 } 126 127 protected void applyCompatibility(Action finalAction) { 128 if (finalAction != null && finalAction.getType() == null) { 129 // iterate over all categories to apply compat 130 String[] cats = finalAction.getCategories(); 131 if (cats != null) { 132 for (String cat : cats) { 133 if (applyCompatibility(cat, finalAction)) { 134 break; 135 } 136 } 137 } 138 } 139 } 140 141 protected boolean applyCompatibility(String category, Action finalAction) { 142 if (finalAction != null && finalAction.getType() == null) { 143 for (TypeCompatibility compat : typeCategoryRelations) { 144 for (String compatCategory : compat.getCategories()) { 145 if (StringUtils.equals(compatCategory, category)) { 146 finalAction.setType(compat.getType()); 147 if (applyCustomCompatibility(compat.getType(), finalAction)) { 148 return true; 149 } 150 } 151 } 152 } 153 } 154 return false; 155 } 156 157 /** 158 * Displays specific help messages for migration of actions. 159 * 160 * @since 6.0 161 */ 162 protected boolean applyCustomCompatibility(String compatType, Action action) { 163 // 6.0 BBB: home/admin tab actions migrated to widgets 164 if ("admin_rest_document_link".equals(compatType) || "home_rest_document_link".equals(compatType)) { 165 boolean applied = false; 166 String link = action.getLink(); 167 if (link != null && !link.startsWith("/")) { 168 action.setLink("/" + link); 169 applied = true; 170 } 171 if (applied) { 172 log.warn(String.format( 173 "Applied compatibility to action '%s', its configuration " 174 + "should be reviewed: make sure the link references an " + "absolute path", 175 action.getId())); 176 return true; 177 } 178 } 179 return false; 180 } 181 182 public synchronized Action getAction(String id) { 183 Action action = actions.get(id); 184 Action finalAction = getClonedAction(action); 185 applyCompatibility(finalAction); 186 return finalAction; 187 } 188 189 protected Action getClonedAction(Action action) { 190 if (action == null) { 191 return null; 192 } 193 return action.clone(); 194 } 195 196 protected static List<Action> sortActions(Collection<Action> actions) { 197 List<Action> sortedActions = new ArrayList<>(); 198 if (actions != null) { 199 sortedActions.addAll(actions); 200 Collections.sort(sortedActions); 201 } 202 return sortedActions; 203 } 204 205 public List<TypeCompatibility> getTypeCategoryRelations() { 206 return typeCategoryRelations; 207 } 208 209 public void setTypeCategoryRelations(List<TypeCompatibility> typeCategoryRelations) { 210 this.typeCategoryRelations = typeCategoryRelations; 211 } 212 213}