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