001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.verify;
016    
017    import com.liferay.portal.kernel.dao.db.DB;
018    import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
019    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
020    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
021    import com.liferay.portal.kernel.exception.PortalException;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.HtmlUtil;
029    import com.liferay.portal.kernel.util.HttpUtil;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.kernel.workflow.WorkflowConstants;
035    import com.liferay.portal.kernel.xml.Document;
036    import com.liferay.portal.kernel.xml.Element;
037    import com.liferay.portal.kernel.xml.Node;
038    import com.liferay.portal.kernel.xml.SAXReaderUtil;
039    import com.liferay.portal.service.ResourceLocalServiceUtil;
040    import com.liferay.portal.util.PortalInstances;
041    import com.liferay.portlet.PortletPreferencesFactoryUtil;
042    import com.liferay.portlet.asset.model.AssetEntry;
043    import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
044    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
045    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
046    import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
047    import com.liferay.portlet.journal.model.JournalArticle;
048    import com.liferay.portlet.journal.model.JournalArticleConstants;
049    import com.liferay.portlet.journal.model.JournalContentSearch;
050    import com.liferay.portlet.journal.model.JournalFolder;
051    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
052    import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
053    import com.liferay.portlet.journal.service.JournalFolderLocalServiceUtil;
054    import com.liferay.portlet.journal.service.persistence.JournalArticleActionableDynamicQuery;
055    
056    import java.sql.Connection;
057    import java.sql.PreparedStatement;
058    import java.sql.ResultSet;
059    
060    import java.util.List;
061    import java.util.regex.Pattern;
062    
063    import javax.portlet.PortletPreferences;
064    
065    /**
066     * @author Alexander Chow
067     * @author Shinn Lok
068     */
069    public class VerifyJournal extends VerifyProcess {
070    
071            public static final long DEFAULT_GROUP_ID = 14;
072    
073            public static final int NUM_OF_ARTICLES = 5;
074    
075            @Override
076            protected void doVerify() throws Exception {
077                    verifyContent();
078                    updateFolderAssets();
079                    verifyOracleNewLine();
080                    verifyPermissionsAndAssets();
081                    verifySearch();
082                    verifyTree();
083                    verifyURLTitle();
084            }
085    
086            protected void updateElements(List<Element> elements) throws Exception {
087                    for (Element element : elements) {
088                            String type = element.attributeValue("type");
089    
090                            if (!type.equals("document_library")) {
091                                    continue;
092                            }
093    
094                            updateElements(element.elements("dynamic-element"));
095    
096                            Element dynamicContentElement = element.element("dynamic-content");
097    
098                            String path = dynamicContentElement.getStringValue();
099    
100                            String[] pathArray = StringUtil.split(path, CharPool.SLASH);
101    
102                            if (pathArray.length != 5) {
103                                    continue;
104                            }
105    
106                            long groupId = GetterUtil.getLong(pathArray[2]);
107                            long folderId = GetterUtil.getLong(pathArray[3]);
108                            String title = HttpUtil.decodeURL(HtmlUtil.escape(pathArray[4]));
109    
110                            if (title.contains(StringPool.SLASH)) {
111                                    title = StringUtil.replace(
112                                            title, StringPool.SLASH, StringPool.BLANK);
113    
114                                    StringBundler sb = new StringBundler(9);
115    
116                                    for (int i = 0; i < 4; i++) {
117                                            sb.append(pathArray[i]);
118                                            sb.append(StringPool.SLASH);
119                                    }
120    
121                                    sb.append(title);
122    
123                                    path = sb.toString();
124                            }
125    
126                            DLFileEntry dlFileEntry =
127                                    DLFileEntryLocalServiceUtil.fetchFileEntry(
128                                            groupId, folderId, title);
129    
130                            if (dlFileEntry == null) {
131                                    continue;
132                            }
133    
134                            Node node = dynamicContentElement.node(0);
135    
136                            node.setText(path + StringPool.SLASH + dlFileEntry.getUuid());
137                    }
138            }
139    
140            protected void updateFolderAssets() throws Exception {
141                    List<JournalFolder> folders =
142                            JournalFolderLocalServiceUtil.getNoAssetFolders();
143    
144                    if (_log.isDebugEnabled()) {
145                            _log.debug(
146                                    "Processing " + folders.size() + " folders with no asset");
147                    }
148    
149                    for (JournalFolder folder : folders) {
150                            try {
151                                    JournalFolderLocalServiceUtil.updateAsset(
152                                            folder.getUserId(), folder, null, null, null);
153                            }
154                            catch (Exception e) {
155                                    if (_log.isWarnEnabled()) {
156                                            _log.warn(
157                                                    "Unable to update asset for folder " +
158                                                            folder.getFolderId() + ": " + e.getMessage());
159                                    }
160                            }
161                    }
162    
163                    if (_log.isDebugEnabled()) {
164                            _log.debug("Assets verified for folders");
165                    }
166            }
167    
168            protected void updateURLTitle(
169                            long groupId, String articleId, String urlTitle)
170                    throws Exception {
171    
172                    String normalizedURLTitle = FriendlyURLNormalizerUtil.normalize(
173                            urlTitle, _friendlyURLPattern);
174    
175                    if (urlTitle.equals(normalizedURLTitle)) {
176                            return;
177                    }
178    
179                    normalizedURLTitle = JournalArticleLocalServiceUtil.getUniqueUrlTitle(
180                            groupId, articleId, normalizedURLTitle);
181    
182                    Connection con = null;
183                    PreparedStatement ps = null;
184    
185                    try {
186                            con = DataAccess.getUpgradeOptimizedConnection();
187    
188                            ps = con.prepareStatement(
189                                    "update JournalArticle set urlTitle = ? where urlTitle = ?");
190    
191                            ps.setString(1, normalizedURLTitle);
192                            ps.setString(2, urlTitle);
193    
194                            ps.executeUpdate();
195                    }
196                    finally {
197                            DataAccess.cleanUp(con, ps);
198                    }
199            }
200    
201            protected void verifyContent() throws Exception {
202                    Connection con = null;
203                    PreparedStatement ps = null;
204                    ResultSet rs = null;
205    
206                    try {
207                            con = DataAccess.getUpgradeOptimizedConnection();
208    
209                            ps = con.prepareStatement(
210                                    "select id_ from JournalArticle where content like " +
211                                            "'%document_library%' and structureId != ''");
212    
213                            rs = ps.executeQuery();
214    
215                            while (rs.next()) {
216                                    long id = rs.getLong("id_");
217    
218                                    JournalArticle article =
219                                            JournalArticleLocalServiceUtil.getArticle(id);
220    
221                                    Document document = SAXReaderUtil.read(article.getContent());
222    
223                                    Element rootElement = document.getRootElement();
224    
225                                    updateElements(rootElement.elements());
226    
227                                    article.setContent(document.asXML());
228    
229                                    JournalArticleLocalServiceUtil.updateJournalArticle(article);
230                            }
231                    }
232                    finally {
233                            DataAccess.cleanUp(con, ps, rs);
234                    }
235            }
236    
237            protected void verifyContentSearch(long groupId, String portletId)
238                    throws Exception {
239    
240                    Connection con = null;
241                    PreparedStatement ps = null;
242                    ResultSet rs = null;
243    
244                    try {
245                            con = DataAccess.getUpgradeOptimizedConnection();
246    
247                            ps = con.prepareStatement(
248                                    "select preferences from PortletPreferences inner join " +
249                                            "Layout on PortletPreferences.plid = Layout.plid where " +
250                                                    "groupId = ? and portletId = ?");
251    
252                            ps.setLong(1, groupId);
253                            ps.setString(2, portletId);
254    
255                            rs = ps.executeQuery();
256    
257                            while (rs.next()) {
258                                    String xml = rs.getString("preferences");
259    
260                                    PortletPreferences portletPreferences =
261                                            PortletPreferencesFactoryUtil.fromDefaultXML(xml);
262    
263                                    String articleId = portletPreferences.getValue(
264                                            "articleId", null);
265    
266                                    List<JournalContentSearch> contentSearches =
267                                            JournalContentSearchLocalServiceUtil.
268                                                    getArticleContentSearches(groupId, articleId);
269    
270                                    if (contentSearches.isEmpty()) {
271                                            continue;
272                                    }
273    
274                                    JournalContentSearch contentSearch = contentSearches.get(0);
275    
276                                    JournalContentSearchLocalServiceUtil.updateContentSearch(
277                                            contentSearch.getGroupId(), contentSearch.isPrivateLayout(),
278                                            contentSearch.getLayoutId(), contentSearch.getPortletId(),
279                                            articleId, true);
280                            }
281                    }
282                    finally {
283                            DataAccess.cleanUp(con, ps, rs);
284                    }
285            }
286    
287            protected void verifyOracleNewLine() throws Exception {
288                    DB db = DBFactoryUtil.getDB();
289    
290                    String dbType = db.getType();
291    
292                    if (!dbType.equals(DB.TYPE_ORACLE)) {
293                            return;
294                    }
295    
296                    // This is a workaround for a limitation in Oracle sqlldr's inability
297                    // insert new line characters for long varchar columns. See
298                    // http://forums.liferay.com/index.php?showtopic=2761&hl=oracle for more
299                    // information. Check several articles because some articles may not
300                    // have new lines.
301    
302                    boolean checkNewLine = false;
303    
304                    List<JournalArticle> articles =
305                            JournalArticleLocalServiceUtil.getArticles(
306                                    DEFAULT_GROUP_ID, 0, NUM_OF_ARTICLES);
307    
308                    for (JournalArticle article : articles) {
309                            String content = article.getContent();
310    
311                            if ((content != null) && content.contains("\\n")) {
312                                    articles = JournalArticleLocalServiceUtil.getArticles(
313                                            DEFAULT_GROUP_ID);
314    
315                                    for (int j = 0; j < articles.size(); j++) {
316                                            article = articles.get(j);
317    
318                                            JournalArticleLocalServiceUtil.checkNewLine(
319                                                    article.getGroupId(), article.getArticleId(),
320                                                    article.getVersion());
321                                    }
322    
323                                    checkNewLine = true;
324    
325                                    break;
326                            }
327                    }
328    
329                    // Only process this once
330    
331                    if (!checkNewLine) {
332                            if (_log.isInfoEnabled()) {
333                                    _log.info("Do not fix oracle new line");
334                            }
335    
336                            return;
337                    }
338                    else {
339                            if (_log.isInfoEnabled()) {
340                                    _log.info("Fix oracle new line");
341                            }
342                    }
343            }
344    
345            protected void verifyPermissionsAndAssets() throws Exception {
346                    ActionableDynamicQuery actionableDynamicQuery =
347                            new JournalArticleActionableDynamicQuery() {
348    
349                            @Override
350                            protected void performAction(Object object)
351                                    throws PortalException, SystemException {
352    
353                                    JournalArticle article = (JournalArticle)object;
354    
355                                    long groupId = article.getGroupId();
356                                    String articleId = article.getArticleId();
357                                    double version = article.getVersion();
358                                    String structureId = article.getStructureId();
359    
360                                    if (article.getResourcePrimKey() <= 0) {
361                                            article =
362                                                    JournalArticleLocalServiceUtil.
363                                                            checkArticleResourcePrimKey(
364                                                                    groupId, articleId, version);
365                                    }
366    
367                                    ResourceLocalServiceUtil.addResources(
368                                            article.getCompanyId(), 0, 0,
369                                            JournalArticle.class.getName(),
370                                            article.getResourcePrimKey(), false, false, false);
371    
372                                    AssetEntry assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
373                                            JournalArticle.class.getName(),
374                                            article.getResourcePrimKey());
375    
376                                    if (assetEntry == null) {
377                                            try {
378                                                    JournalArticleLocalServiceUtil.updateAsset(
379                                                            article.getUserId(), article, null, null, null);
380                                            }
381                                            catch (Exception e) {
382                                                    if (_log.isWarnEnabled()) {
383                                                            _log.warn(
384                                                                    "Unable to update asset for article " +
385                                                                            article.getId() + ": " + e.getMessage());
386                                                    }
387                                            }
388                                    }
389                                    else if ((article.getStatus() ==
390                                                            WorkflowConstants.STATUS_DRAFT) &&
391                                                     (article.getVersion() ==
392                                                            JournalArticleConstants.VERSION_DEFAULT)) {
393    
394                                            AssetEntryLocalServiceUtil.updateEntry(
395                                                    assetEntry.getClassName(), assetEntry.getClassPK(),
396                                                    null, assetEntry.isVisible());
397                                    }
398    
399                                    if (Validator.isNotNull(structureId)) {
400                                            /*JournalStructure structure =
401                                                    JournalStructureLocalServiceUtil.getStructure(
402                                                            groupId, structureId);
403    
404                                            newContent = JournalUtil.removeOldContent(
405                                                    newContent, structure.getXsd());*/
406                                    }
407    
408                                    try {
409                                            JournalArticleLocalServiceUtil.checkStructure(
410                                                    groupId, articleId, version);
411                                    }
412                                    catch (NoSuchStructureException nsse) {
413                                            if (_log.isWarnEnabled()) {
414                                                    _log.warn(
415                                                            "Removing reference to missing structure for " +
416                                                                    "article " + article.getId());
417                                            }
418    
419                                            article.setStructureId(StringPool.BLANK);
420                                            article.setTemplateId(StringPool.BLANK);
421    
422                                            JournalArticleLocalServiceUtil.updateJournalArticle(
423                                                    article);
424                                    }
425                            }
426    
427                    };
428    
429                    actionableDynamicQuery.performActions();
430    
431                    if (_log.isDebugEnabled()) {
432                            _log.debug("Permissions and assets verified for articles");
433                    }
434            }
435    
436            protected void verifySearch() throws Exception {
437                    Connection con = null;
438                    PreparedStatement ps = null;
439                    ResultSet rs = null;
440    
441                    try {
442                            con = DataAccess.getUpgradeOptimizedConnection();
443    
444                            ps = con.prepareStatement(
445                                    "select groupId, portletId from JournalContentSearch group " +
446                                            "by groupId, portletId having count(groupId) > 1 and " +
447                                                    "count(portletId) > 1");
448    
449                            rs = ps.executeQuery();
450    
451                            while (rs.next()) {
452                                    long groupId = rs.getLong("groupId");
453                                    String portletId = rs.getString("portletId");
454    
455                                    verifyContentSearch(groupId, portletId);
456                            }
457                    }
458                    finally {
459                            DataAccess.cleanUp(con, ps, rs);
460                    }
461            }
462    
463            protected void verifyTree() throws Exception {
464                    long[] companyIds = PortalInstances.getCompanyIdsBySQL();
465    
466                    for (long companyId : companyIds) {
467                            JournalArticleLocalServiceUtil.rebuildTree(companyId);
468                            JournalFolderLocalServiceUtil.rebuildTree(companyId);
469                    }
470            }
471    
472            protected void verifyURLTitle() throws Exception {
473                    Connection con = null;
474                    PreparedStatement ps = null;
475                    ResultSet rs = null;
476    
477                    try {
478                            con = DataAccess.getUpgradeOptimizedConnection();
479    
480                            ps = con.prepareStatement(
481                                    "select distinct groupId, articleId, urlTitle from " +
482                                            "JournalArticle");
483    
484                            rs = ps.executeQuery();
485    
486                            while (rs.next()) {
487                                    long groupId = rs.getLong("groupId");
488                                    String articleId = rs.getString("articleId");
489                                    String urlTitle = GetterUtil.getString(
490                                            rs.getString("urlTitle"));
491    
492                                    updateURLTitle(groupId, articleId, urlTitle);
493                            }
494                    }
495                    finally {
496                            DataAccess.cleanUp(con, ps, rs);
497                    }
498            }
499    
500            private static Log _log = LogFactoryUtil.getLog(VerifyJournal.class);
501    
502            private static Pattern _friendlyURLPattern = Pattern.compile("[^a-z0-9_-]");
503    
504    }