001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (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.gwt;
029
030import org.opencms.db.CmsAlias;
031import org.opencms.db.CmsAliasManager;
032import org.opencms.db.CmsRewriteAlias;
033import org.opencms.file.CmsObject;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.CmsVfsResourceNotFoundException;
037import org.opencms.gwt.shared.alias.CmsAliasBean;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.main.OpenCms;
041import org.opencms.util.CmsUUID;
042
043import java.io.IOException;
044import java.io.StringWriter;
045import java.util.ArrayList;
046import java.util.HashMap;
047import java.util.HashSet;
048import java.util.List;
049import java.util.Locale;
050import java.util.Map;
051import java.util.Set;
052
053import org.apache.commons.logging.Log;
054
055import au.com.bytecode.opencsv.CSVWriter;
056
057/**
058 * This class contains the real implementations of service methods related to aliases.<p>
059 */
060public class CmsAliasHelper {
061
062    /** The logger for this class. */
063    private static final Log LOG = CmsLog.getLog(CmsAliasHelper.class);
064
065    /** The internal CMS object. */
066    private CmsObject m_cms;
067
068    /**
069     * Returns the comma separated alias data.<p>
070     *
071     * @param cms the OpenCms context
072     *
073     * @return the comma separated alias data
074     *
075     * @throws CmsException if something goes wrong reading the alias data
076     */
077    public String exportAliasesAsCsv(CmsObject cms) throws CmsException {
078
079        String siteRoot = cms.getRequestContext().getSiteRoot();
080        List<CmsAlias> aliases = OpenCms.getAliasManager().getAliasesForSite(cms, siteRoot);
081        StringWriter writer = new StringWriter();
082        CSVWriter csvWriter = new CSVWriter(writer);
083        StringBuffer resultBuffer = writer.getBuffer();
084        for (CmsAlias alias : aliases) {
085            try {
086                CmsResource resource = cms.readResource(alias.getStructureId());
087                csvWriter.writeNext(
088                    new String[] {alias.getAliasPath(), cms.getSitePath(resource), alias.getMode().toString()});
089            } catch (CmsException e) {
090                LOG.warn("Could not read alias resource", e);
091                continue;
092            }
093        }
094
095        List<CmsRewriteAlias> rewriteAliases = OpenCms.getAliasManager().getRewriteAliases(cms, siteRoot);
096        for (CmsRewriteAlias rewrite : rewriteAliases) {
097            csvWriter.writeNext(
098                new String[] {
099                    rewrite.getPatternString(),
100                    rewrite.getReplacementString(),
101                    rewrite.getMode().toString(),
102                    "rewrite"});
103        }
104        try {
105            csvWriter.flush();
106            csvWriter.close();
107        } catch (IOException e) {
108            // can't happen
109        }
110        return resultBuffer.toString();
111    }
112
113    /**
114     * Saves aliases.<p>
115     *
116     * @param structureId the structure id
117     * @param aliasBeans the alias beans
118     * @throws CmsException if something goes wrong
119     */
120    public void saveAliases(CmsUUID structureId, List<CmsAliasBean> aliasBeans) throws CmsException {
121
122        CmsAliasManager aliasManager = OpenCms.getAliasManager();
123        CmsObject cms = m_cms;
124        List<CmsAlias> aliases = new ArrayList<CmsAlias>();
125        for (CmsAliasBean aliasBean : aliasBeans) {
126            CmsAlias alias = new CmsAlias(
127                structureId,
128                cms.getRequestContext().getSiteRoot(),
129                aliasBean.getSitePath(),
130                aliasBean.getMode());
131            aliases.add(alias);
132        }
133        aliasManager.saveAliases(cms, structureId, aliases);
134    }
135
136    /**
137     * Sets the CMS object.<p>
138     *
139     * @param cms the CMS object
140     */
141    public void setCms(CmsObject cms) {
142
143        m_cms = cms;
144    }
145
146    /**
147     * Checks whether a given string is a valid alias path.<p>
148     *
149     * @param path the path to check
150     * @param locale the locale to use for validation messages
151     *
152     * @return null if the string is a valid alias path, else an error message
153     */
154    protected String checkValidAliasPath(String path, Locale locale) {
155
156        if (org.opencms.db.CmsAlias.ALIAS_PATTERN.matcher(path).matches()) {
157            return null;
158        } else {
159            return Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_INVALID_PATH_0);
160        }
161    }
162
163    /**
164     * Converts a server-side alias object to an alias bean.<p>
165     *
166     * @param alias the server-side alias object
167     *
168     * @return the client-side alias bean
169     */
170    protected CmsAliasBean convertAliasToBean(CmsAlias alias) {
171
172        return new CmsAliasBean(alias.getAliasPath(), alias.getMode());
173    }
174
175    /**
176     * Implementation of the getAliasesForPage method.<p>
177     *
178     * @param uuid the structure id of the page
179     * @return the aliases for the given page
180     *
181     * @throws CmsException if something goes wrong
182     */
183    protected List<CmsAliasBean> getAliasesForPage(CmsUUID uuid) throws CmsException {
184
185        CmsAliasManager aliasManager = OpenCms.getAliasManager();
186        List<CmsAlias> aliases = aliasManager.getAliasesForStructureId(m_cms, uuid);
187        List<CmsAliasBean> result = new ArrayList<CmsAliasBean>();
188        for (CmsAlias alias : aliases) {
189            CmsAliasBean bean = convertAliasToBean(alias);
190            result.add(bean);
191        }
192        return result;
193    }
194
195    /**
196     * The internal method used for validating aliases.<p>
197     *
198     * @param uuid the structure id of the resource whose aliases are being validated
199     * @param aliasPaths a map from (arbitrary) ids to alias paths
200     *
201     * @return a map from the same ids to validation error messages
202     *
203     * @throws CmsException if something goes wrong
204     */
205    protected Map<String, String> validateAliases(CmsUUID uuid, Map<String, String> aliasPaths) throws CmsException {
206
207        Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
208        Set<String> seenPaths = new HashSet<String>();
209        Set<String> duplicatePaths = new HashSet<String>();
210        for (String path : aliasPaths.values()) {
211            if (seenPaths.contains(path)) {
212                duplicatePaths.add(path);
213            }
214            seenPaths.add(path);
215        }
216        Map<String, String> errorMessagesByPath = new HashMap<String, String>();
217        for (String path : duplicatePaths) {
218            errorMessagesByPath.put(path, Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_DUPLICATE_PATH_0));
219        }
220        seenPaths.removeAll(duplicatePaths);
221
222        for (String path : seenPaths) {
223            String pathError = checkValidAliasPath(path, locale);
224            if (pathError != null) {
225                errorMessagesByPath.put(path, pathError);
226            } else {
227                errorMessagesByPath.put(path, null);
228                if (m_cms.existsResource(path, CmsResourceFilter.ALL)) {
229                    errorMessagesByPath.put(path, Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_IS_VFS_0));
230                } else {
231                    List<CmsAlias> aliases = OpenCms.getAliasManager().getAliasesForPath(
232                        m_cms,
233                        m_cms.getRequestContext().getSiteRoot(),
234                        path);
235                    for (CmsAlias alias : aliases) {
236                        CmsUUID otherStructureId = alias.getStructureId();
237                        if (!otherStructureId.equals(uuid)) {
238                            try {
239                                CmsResource resource = m_cms.readResource(otherStructureId, CmsResourceFilter.ALL);
240                                errorMessagesByPath.put(
241                                    path,
242                                    Messages.get().getBundle(locale).key(
243                                        Messages.ERR_ALIAS_ALREADY_USED_1,
244                                        resource.getRootPath()));
245                                break;
246                            } catch (CmsVfsResourceNotFoundException e) {
247                                // this may happen if there are outdated entries in the database table
248                                errorMessagesByPath.put(
249                                    path,
250                                    Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_ALREADY_USED_UNKNOWN_0));
251                                break;
252                            }
253                        }
254                    }
255                }
256
257            }
258        }
259        Map<String, String> errorMessagesById = new HashMap<String, String>();
260        for (String key : aliasPaths.keySet()) {
261            String path = aliasPaths.get(key);
262            if (errorMessagesByPath.containsKey(path)) {
263                String errorMessage = errorMessagesByPath.get(path);
264                errorMessagesById.put(key, errorMessage);
265            }
266        }
267        return errorMessagesById;
268    }
269}