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.favorites;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResourceFilter;
032import org.opencms.file.CmsUser;
033import org.opencms.json.JSONArray;
034import org.opencms.json.JSONException;
035import org.opencms.json.JSONObject;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.util.CmsStringUtil;
040import org.opencms.util.CmsUUID;
041
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.HashSet;
045import java.util.List;
046import java.util.Set;
047
048import org.apache.commons.logging.Log;
049
050/**
051 * Loads/saves favorites.
052 *
053 * <p>When loading favorites, the individual entries are validated by trying to read the resources/projects they reference.
054 * If this fails, the entries are discareded from the returned list.
055 */
056public class CmsFavoriteDAO {
057
058    /** The additional info key under which the favorites for a user are saved. */
059    public static final String ADDINFO_KEY = "favLocations";
060
061    /** The main key in the JSON object containing the favorites. */
062    public static final String BASE_KEY = "f";
063
064    /** The logger instance for this class. */
065    private static final Log LOG = CmsLog.getLog(CmsFavoriteDAO.class);
066
067    /** The CMS context. */
068    private CmsObject m_cms;
069
070    /** Projects which have been validated. */
071    private Set<CmsUUID> m_okProjects = new HashSet<>();
072
073    /** Site roots which have been validated. */
074    private Set<String> m_okSiteRoots = new HashSet<>();
075
076    /** Structure ids which have been validated. */
077    private Set<CmsUUID> m_okStructureIds = new HashSet<>();
078
079    /** The root CMS context. */
080    private CmsObject m_rootCms;
081
082    /** Name of user from which bookmarks should be loaded. */
083    private String m_userName;
084
085    /**
086     * Creates a new instance.
087     *
088     * @param cms the CMS Context
089     * @throws CmsException if something goes wrong
090     */
091    public CmsFavoriteDAO(CmsObject cms)
092    throws CmsException {
093
094        this(cms, cms.getRequestContext().getCurrentUser().getName());
095    }
096
097    /**
098     * Creates a new instance.
099     *
100     * @param cms the CMS Context
101     * @param userName the name of the user in whose additional infos the bookmarks are stored
102     * @throws CmsException if something goes wrong
103     */
104    public CmsFavoriteDAO(CmsObject cms, String userName)
105    throws CmsException {
106
107        m_cms = cms;
108        m_rootCms = OpenCms.initCmsObject(m_cms);
109        m_rootCms.getRequestContext().setSiteRoot("");
110        m_userName = userName;
111    }
112
113    /**
114     * Loads the favorite list.
115     *
116     * @return the list of favorites
117     *
118     * @throws CmsException if something goes wrong
119     */
120    public List<CmsFavoriteEntry> loadFavorites() throws CmsException {
121
122        List<CmsFavoriteEntry> result = new ArrayList<>();
123        try {
124            CmsUser user = readUser();
125            String data = (String)user.getAdditionalInfo(ADDINFO_KEY);
126            if (CmsStringUtil.isEmptyOrWhitespaceOnly(data)) {
127                return new ArrayList<>();
128            }
129            JSONObject json = new JSONObject(data);
130            JSONArray array = json.getJSONArray(BASE_KEY);
131            for (int i = 0; i < array.length(); i++) {
132                JSONObject fav = array.getJSONObject(i);
133                try {
134                    CmsFavoriteEntry entry = new CmsFavoriteEntry(fav);
135                    if (validate(entry)) {
136                        result.add(entry);
137                    }
138                } catch (Exception e) {
139                    LOG.warn(e.getLocalizedMessage(), e);
140                }
141
142            }
143        } catch (JSONException e) {
144            LOG.error(e.getLocalizedMessage(), e);
145        }
146        return result;
147    }
148
149    /**
150     * Saves the favorites.
151     *
152     * @param favorites the list of favorites to save
153     * @throws CmsException if something goes wrong
154     */
155    public void saveFavorites(List<CmsFavoriteEntry> favorites) throws CmsException {
156
157        try {
158            JSONObject json = new JSONObject();
159            JSONArray array = new JSONArray();
160            for (CmsFavoriteEntry entry : favorites) {
161                array.put(entry.toJson());
162            }
163            json.put(BASE_KEY, array);
164            String data = json.toString();
165            CmsUser user = readUser();
166            user.setAdditionalInfo(ADDINFO_KEY, data);
167            m_cms.writeUser(user);
168        } catch (JSONException e) {
169            LOG.error(e.getLocalizedMessage(), e);
170        }
171
172    }
173
174    private CmsUser readUser() throws CmsException {
175
176        return m_cms.readUser(m_userName);
177
178    }
179
180    /**
181     * Validates a favorite entry.
182     *
183     * <p>If the favorite entry references a resource or project that can't be read, this will return false.
184     *
185     * @param entry the favorite entry
186     * @return the
187     */
188    private boolean validate(CmsFavoriteEntry entry) {
189
190        try {
191            String siteRoot = entry.getSiteRoot();
192            if (!m_okSiteRoots.contains(siteRoot)) {
193                m_rootCms.readResource(siteRoot);
194                m_okSiteRoots.add(siteRoot);
195            }
196            CmsUUID project = entry.getProjectId();
197            if (!m_okProjects.contains(project)) {
198                m_cms.readProject(project);
199                m_okProjects.add(project);
200            }
201            for (CmsUUID id : Arrays.asList(entry.getDetailId(), entry.getStructureId())) {
202                if ((id != null) && !m_okStructureIds.contains(id)) {
203                    m_cms.readResource(id, CmsResourceFilter.IGNORE_EXPIRATION.addRequireVisible());
204                    m_okStructureIds.add(id);
205                }
206            }
207            return true;
208
209        } catch (Exception e) {
210            LOG.info("Favorite entry validation failed: " + e.getLocalizedMessage(), e);
211            return false;
212        }
213
214    }
215
216}