001    /**
002     * Copyright (c) 2000-present 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.dao.orm.QueryUtil;
022    import com.liferay.portal.kernel.exception.PortalException;
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.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.workflow.WorkflowConstants;
033    import com.liferay.portal.kernel.xml.Document;
034    import com.liferay.portal.kernel.xml.Element;
035    import com.liferay.portal.kernel.xml.Node;
036    import com.liferay.portal.kernel.xml.SAXReaderUtil;
037    import com.liferay.portal.service.ResourceLocalServiceUtil;
038    import com.liferay.portal.util.PortalInstances;
039    import com.liferay.portlet.PortletPreferencesFactoryUtil;
040    import com.liferay.portlet.asset.model.AssetEntry;
041    import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
042    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
043    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
044    import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
045    import com.liferay.portlet.journal.model.JournalArticle;
046    import com.liferay.portlet.journal.model.JournalArticleConstants;
047    import com.liferay.portlet.journal.model.JournalArticleResource;
048    import com.liferay.portlet.journal.model.JournalContentSearch;
049    import com.liferay.portlet.journal.model.JournalFolder;
050    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
051    import com.liferay.portlet.journal.service.JournalArticleResourceLocalServiceUtil;
052    import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
053    import com.liferay.portlet.journal.service.JournalFolderLocalServiceUtil;
054    import com.liferay.portlet.journal.util.comparator.ArticleVersionComparator;
055    
056    import java.sql.Connection;
057    import java.sql.PreparedStatement;
058    import java.sql.ResultSet;
059    
060    import java.util.Date;
061    import java.util.List;
062    import java.util.regex.Pattern;
063    
064    import javax.portlet.PortletPreferences;
065    
066    /**
067     * @author Alexander Chow
068     * @author Shinn Lok
069     */
070    public class VerifyJournal extends VerifyProcess {
071    
072            public static final long DEFAULT_GROUP_ID = 14;
073    
074            public static final int NUM_OF_ARTICLES = 5;
075    
076            @Override
077            protected void doVerify() throws Exception {
078                    verifyContent();
079                    verifyCreateDate();
080                    updateFolderAssets();
081                    verifyOracleNewLine();
082                    verifyPermissionsAndAssets();
083                    verifySearch();
084                    verifyTree();
085                    verifyURLTitle();
086            }
087    
088            protected void updateDocumentLibraryElements(Element element) {
089                    List<Element> dynamicElementElements = element.elements(
090                            "dynamic-element");
091    
092                    for (Element dynamicElementElement : dynamicElementElements) {
093                            updateDocumentLibraryElements(dynamicElementElement);
094                    }
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                            return;
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                    DLFileEntry dlFileEntry = DLFileEntryLocalServiceUtil.fetchFileEntry(
111                            groupId, folderId, title);
112    
113                    if (dlFileEntry == null) {
114                            return;
115                    }
116    
117                    Node node = dynamicContentElement.node(0);
118    
119                    node.setText(path + StringPool.SLASH + dlFileEntry.getUuid());
120            }
121    
122            protected void updateFolderAssets() throws Exception {
123                    List<JournalFolder> folders =
124                            JournalFolderLocalServiceUtil.getNoAssetFolders();
125    
126                    if (_log.isDebugEnabled()) {
127                            _log.debug(
128                                    "Processing " + folders.size() + " folders with no asset");
129                    }
130    
131                    for (JournalFolder folder : folders) {
132                            try {
133                                    JournalFolderLocalServiceUtil.updateAsset(
134                                            folder.getUserId(), folder, null, null, null);
135                            }
136                            catch (Exception e) {
137                                    if (_log.isWarnEnabled()) {
138                                            _log.warn(
139                                                    "Unable to update asset for folder " +
140                                                            folder.getFolderId() + ": " + e.getMessage());
141                                    }
142                            }
143                    }
144    
145                    if (_log.isDebugEnabled()) {
146                            _log.debug("Assets verified for folders");
147                    }
148            }
149    
150            protected void updateLinkToLayoutElements(long groupId, Element element) {
151                    List<Element> dynamicElementElements = element.elements(
152                            "dynamic-element");
153    
154                    for (Element dynamicElementElement : dynamicElementElements) {
155                            updateLinkToLayoutElements(groupId, dynamicElementElement);
156                    }
157    
158                    Element dynamicContentElement = element.element("dynamic-content");
159    
160                    Node node = dynamicContentElement.node(0);
161    
162                    node.setText(
163                            dynamicContentElement.getStringValue() + StringPool.AT + groupId);
164            }
165    
166            protected void updateURLTitle(
167                            long groupId, String articleId, String urlTitle)
168                    throws Exception {
169    
170                    String normalizedURLTitle = FriendlyURLNormalizerUtil.normalize(
171                            urlTitle, _friendlyURLPattern);
172    
173                    if (urlTitle.equals(normalizedURLTitle)) {
174                            return;
175                    }
176    
177                    normalizedURLTitle = JournalArticleLocalServiceUtil.getUniqueUrlTitle(
178                            groupId, articleId, normalizedURLTitle);
179    
180                    Connection con = null;
181                    PreparedStatement ps = null;
182    
183                    try {
184                            con = DataAccess.getUpgradeOptimizedConnection();
185    
186                            ps = con.prepareStatement(
187                                    "update JournalArticle set urlTitle = ? where urlTitle = ?");
188    
189                            ps.setString(1, normalizedURLTitle);
190                            ps.setString(2, urlTitle);
191    
192                            ps.executeUpdate();
193                    }
194                    finally {
195                            DataAccess.cleanUp(con, ps);
196                    }
197            }
198    
199            protected void verifyContent() throws Exception {
200                    Connection con = null;
201                    PreparedStatement ps = null;
202                    ResultSet rs = null;
203    
204                    try {
205                            con = DataAccess.getUpgradeOptimizedConnection();
206    
207                            ps = con.prepareStatement(
208                                    "select id_ from JournalArticle where (content like " +
209                                            "'%document_library%' or content like '%link_to_layout%')" +
210                                                    " and structureId != ''");
211    
212                            rs = ps.executeQuery();
213    
214                            while (rs.next()) {
215                                    long id = rs.getLong("id_");
216    
217                                    JournalArticle article =
218                                            JournalArticleLocalServiceUtil.getArticle(id);
219    
220                                    Document document = SAXReaderUtil.read(article.getContent());
221    
222                                    Element rootElement = document.getRootElement();
223    
224                                    for (Element element : rootElement.elements()) {
225                                            String type = element.attributeValue("type");
226    
227                                            if (type.equals("document_library")) {
228                                                    updateDocumentLibraryElements(element);
229                                            }
230                                            else if (type.equals("link_to_layout")) {
231                                                    updateLinkToLayoutElements(
232                                                            article.getGroupId(), element);
233                                            }
234                                    }
235    
236                                    article.setContent(document.asXML());
237    
238                                    JournalArticleLocalServiceUtil.updateJournalArticle(article);
239                            }
240                    }
241                    finally {
242                            DataAccess.cleanUp(con, ps, rs);
243                    }
244            }
245    
246            protected void verifyContentSearch(long groupId, String portletId)
247                    throws Exception {
248    
249                    Connection con = null;
250                    PreparedStatement ps = null;
251                    ResultSet rs = null;
252    
253                    try {
254                            con = DataAccess.getUpgradeOptimizedConnection();
255    
256                            ps = con.prepareStatement(
257                                    "select preferences from PortletPreferences inner join " +
258                                            "Layout on PortletPreferences.plid = Layout.plid where " +
259                                                    "groupId = ? and portletId = ?");
260    
261                            ps.setLong(1, groupId);
262                            ps.setString(2, portletId);
263    
264                            rs = ps.executeQuery();
265    
266                            while (rs.next()) {
267                                    String xml = rs.getString("preferences");
268    
269                                    PortletPreferences portletPreferences =
270                                            PortletPreferencesFactoryUtil.fromDefaultXML(xml);
271    
272                                    String articleId = portletPreferences.getValue(
273                                            "articleId", null);
274    
275                                    List<JournalContentSearch> contentSearches =
276                                            JournalContentSearchLocalServiceUtil.
277                                                    getArticleContentSearches(groupId, articleId);
278    
279                                    if (contentSearches.isEmpty()) {
280                                            continue;
281                                    }
282    
283                                    JournalContentSearch contentSearch = contentSearches.get(0);
284    
285                                    JournalContentSearchLocalServiceUtil.updateContentSearch(
286                                            contentSearch.getGroupId(), contentSearch.isPrivateLayout(),
287                                            contentSearch.getLayoutId(), contentSearch.getPortletId(),
288                                            articleId, true);
289                            }
290                    }
291                    finally {
292                            DataAccess.cleanUp(con, ps, rs);
293                    }
294            }
295    
296            protected void verifyCreateDate() throws Exception {
297                    ActionableDynamicQuery actionableDynamicQuery =
298                            JournalArticleResourceLocalServiceUtil.getActionableDynamicQuery();
299    
300                    actionableDynamicQuery.setPerformActionMethod(
301                            new ActionableDynamicQuery.PerformActionMethod() {
302    
303                                    @Override
304                                    public void performAction(Object object) {
305                                            JournalArticleResource articleResource =
306                                                    (JournalArticleResource)object;
307    
308                                            verifyCreateDate(articleResource);
309                                    }
310    
311                            });
312    
313                    actionableDynamicQuery.performActions();
314    
315                    if (_log.isDebugEnabled()) {
316                            _log.debug("Create dates verified for articles");
317                    }
318            }
319    
320            protected void verifyCreateDate(JournalArticleResource articleResource) {
321                    List<JournalArticle> articles =
322                            JournalArticleLocalServiceUtil.getArticles(
323                                    articleResource.getGroupId(), articleResource.getArticleId(),
324                                    QueryUtil.ALL_POS, QueryUtil.ALL_POS,
325                                    new ArticleVersionComparator(true));
326    
327                    if (articles.size() <= 1) {
328                            return;
329                    }
330    
331                    JournalArticle firstArticle = articles.get(0);
332    
333                    Date createDate = firstArticle.getCreateDate();
334    
335                    for (JournalArticle article : articles) {
336                            if (!createDate.equals(article.getCreateDate())) {
337                                    article.setCreateDate(createDate);
338    
339                                    JournalArticleLocalServiceUtil.updateJournalArticle(article);
340                            }
341                    }
342            }
343    
344            protected void verifyOracleNewLine() throws Exception {
345                    DB db = DBFactoryUtil.getDB();
346    
347                    String dbType = db.getType();
348    
349                    if (!dbType.equals(DB.TYPE_ORACLE)) {
350                            return;
351                    }
352    
353                    // This is a workaround for a limitation in Oracle sqlldr's inability
354                    // insert new line characters for long varchar columns. See
355                    // http://forums.liferay.com/index.php?showtopic=2761&hl=oracle for more
356                    // information. Check several articles because some articles may not
357                    // have new lines.
358    
359                    boolean checkNewLine = false;
360    
361                    List<JournalArticle> articles =
362                            JournalArticleLocalServiceUtil.getArticles(
363                                    DEFAULT_GROUP_ID, 0, NUM_OF_ARTICLES);
364    
365                    for (JournalArticle article : articles) {
366                            String content = article.getContent();
367    
368                            if ((content != null) && content.contains("\\n")) {
369                                    articles = JournalArticleLocalServiceUtil.getArticles(
370                                            DEFAULT_GROUP_ID);
371    
372                                    for (int j = 0; j < articles.size(); j++) {
373                                            article = articles.get(j);
374    
375                                            JournalArticleLocalServiceUtil.checkNewLine(
376                                                    article.getGroupId(), article.getArticleId(),
377                                                    article.getVersion());
378                                    }
379    
380                                    checkNewLine = true;
381    
382                                    break;
383                            }
384                    }
385    
386                    // Only process this once
387    
388                    if (!checkNewLine) {
389                            if (_log.isInfoEnabled()) {
390                                    _log.info("Do not fix oracle new line");
391                            }
392    
393                            return;
394                    }
395                    else {
396                            if (_log.isInfoEnabled()) {
397                                    _log.info("Fix oracle new line");
398                            }
399                    }
400            }
401    
402            protected void verifyPermissionsAndAssets() throws Exception {
403                    ActionableDynamicQuery actionableDynamicQuery =
404                            JournalArticleLocalServiceUtil.getActionableDynamicQuery();
405    
406                    actionableDynamicQuery.setPerformActionMethod(
407                            new ActionableDynamicQuery.PerformActionMethod() {
408    
409                                    @Override
410                                    public void performAction(Object object)
411                                            throws PortalException {
412    
413                                            JournalArticle article = (JournalArticle)object;
414    
415                                            verifyPermissionsAndAssets(article);
416                                    }
417    
418                            });
419    
420                    actionableDynamicQuery.performActions();
421    
422                    if (_log.isDebugEnabled()) {
423                            _log.debug("Permissions and assets verified for articles");
424                    }
425            }
426    
427            protected void verifyPermissionsAndAssets(JournalArticle article)
428                    throws PortalException {
429    
430                    long groupId = article.getGroupId();
431                    String articleId = article.getArticleId();
432                    double version = article.getVersion();
433    
434                    if (article.getResourcePrimKey() <= 0) {
435                            article =
436                                    JournalArticleLocalServiceUtil.checkArticleResourcePrimKey(
437                                            groupId, articleId, version);
438                    }
439    
440                    ResourceLocalServiceUtil.addResources(
441                            article.getCompanyId(), 0, 0, JournalArticle.class.getName(),
442                            article.getResourcePrimKey(), false, false, false);
443    
444                    AssetEntry assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
445                            JournalArticle.class.getName(), article.getResourcePrimKey());
446    
447                    if (assetEntry == null) {
448                            try {
449                                    JournalArticleLocalServiceUtil.updateAsset(
450                                            article.getUserId(), article, null, null, null);
451                            }
452                            catch (Exception e) {
453                                    if (_log.isWarnEnabled()) {
454                                            _log.warn(
455                                                    "Unable to update asset for article " +
456                                                            article.getId() + ": " + e.getMessage());
457                                    }
458                            }
459                    }
460                    else if ((article.getStatus() ==
461                                            WorkflowConstants.STATUS_DRAFT) &&
462                                     (article.getVersion() ==
463                                            JournalArticleConstants.VERSION_DEFAULT)) {
464    
465                            AssetEntryLocalServiceUtil.updateEntry(
466                                    assetEntry.getClassName(), assetEntry.getClassPK(), null,
467                                    assetEntry.isVisible());
468                    }
469    
470                    try {
471                            JournalArticleLocalServiceUtil.checkStructure(
472                                    groupId, articleId, version);
473                    }
474                    catch (NoSuchStructureException nsse) {
475                            if (_log.isWarnEnabled()) {
476                                    _log.warn(
477                                            "Removing reference to missing structure for article " +
478                                                    article.getId());
479                            }
480    
481                            article.setStructureId(StringPool.BLANK);
482                            article.setTemplateId(StringPool.BLANK);
483    
484                            JournalArticleLocalServiceUtil.updateJournalArticle(article);
485                    }
486                    catch (Exception e) {
487                            _log.error(
488                                    "Unable to check the structure for article " + article.getId(),
489                                    e);
490                    }
491            }
492    
493            protected void verifySearch() throws Exception {
494                    Connection con = null;
495                    PreparedStatement ps = null;
496                    ResultSet rs = null;
497    
498                    try {
499                            con = DataAccess.getUpgradeOptimizedConnection();
500    
501                            ps = con.prepareStatement(
502                                    "select groupId, portletId from JournalContentSearch group " +
503                                            "by groupId, portletId having count(groupId) > 1 and " +
504                                                    "count(portletId) > 1");
505    
506                            rs = ps.executeQuery();
507    
508                            while (rs.next()) {
509                                    long groupId = rs.getLong("groupId");
510                                    String portletId = rs.getString("portletId");
511    
512                                    verifyContentSearch(groupId, portletId);
513                            }
514                    }
515                    finally {
516                            DataAccess.cleanUp(con, ps, rs);
517                    }
518            }
519    
520            protected void verifyTree() throws Exception {
521                    long[] companyIds = PortalInstances.getCompanyIdsBySQL();
522    
523                    for (long companyId : companyIds) {
524                            JournalFolderLocalServiceUtil.rebuildTree(companyId);
525                    }
526            }
527    
528            protected void verifyURLTitle() throws Exception {
529                    Connection con = null;
530                    PreparedStatement ps = null;
531                    ResultSet rs = null;
532    
533                    try {
534                            con = DataAccess.getUpgradeOptimizedConnection();
535    
536                            ps = con.prepareStatement(
537                                    "select distinct groupId, articleId, urlTitle from " +
538                                            "JournalArticle");
539    
540                            rs = ps.executeQuery();
541    
542                            while (rs.next()) {
543                                    long groupId = rs.getLong("groupId");
544                                    String articleId = rs.getString("articleId");
545                                    String urlTitle = GetterUtil.getString(
546                                            rs.getString("urlTitle"));
547    
548                                    updateURLTitle(groupId, articleId, urlTitle);
549                            }
550                    }
551                    finally {
552                            DataAccess.cleanUp(con, ps, rs);
553                    }
554            }
555    
556            private static Log _log = LogFactoryUtil.getLog(VerifyJournal.class);
557    
558            private static Pattern _friendlyURLPattern = Pattern.compile("[^a-z0-9_-]");
559    
560    }