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