001
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.DynamicQuery;
022 import com.liferay.portal.kernel.dao.orm.Property;
023 import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
024 import com.liferay.portal.kernel.dao.orm.QueryUtil;
025 import com.liferay.portal.kernel.exception.PortalException;
026 import com.liferay.portal.kernel.log.Log;
027 import com.liferay.portal.kernel.log.LogFactoryUtil;
028 import com.liferay.portal.kernel.util.CharPool;
029 import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
030 import com.liferay.portal.kernel.util.GetterUtil;
031 import com.liferay.portal.kernel.util.HtmlUtil;
032 import com.liferay.portal.kernel.util.HttpUtil;
033 import com.liferay.portal.kernel.util.StringPool;
034 import com.liferay.portal.kernel.util.StringUtil;
035 import com.liferay.portal.kernel.workflow.WorkflowConstants;
036 import com.liferay.portal.kernel.xml.Document;
037 import com.liferay.portal.kernel.xml.Element;
038 import com.liferay.portal.kernel.xml.Node;
039 import com.liferay.portal.kernel.xml.SAXReaderUtil;
040 import com.liferay.portal.service.ResourceLocalServiceUtil;
041 import com.liferay.portal.util.PortalInstances;
042 import com.liferay.portlet.PortletPreferencesFactoryUtil;
043 import com.liferay.portlet.asset.model.AssetEntry;
044 import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
045 import com.liferay.portlet.documentlibrary.model.DLFileEntry;
046 import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
047 import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
048 import com.liferay.portlet.dynamicdatamapping.util.DDMFieldsCounter;
049 import com.liferay.portlet.journal.model.JournalArticle;
050 import com.liferay.portlet.journal.model.JournalArticleConstants;
051 import com.liferay.portlet.journal.model.JournalArticleImage;
052 import com.liferay.portlet.journal.model.JournalArticleResource;
053 import com.liferay.portlet.journal.model.JournalContentSearch;
054 import com.liferay.portlet.journal.model.JournalFolder;
055 import com.liferay.portlet.journal.service.JournalArticleImageLocalServiceUtil;
056 import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
057 import com.liferay.portlet.journal.service.JournalArticleResourceLocalServiceUtil;
058 import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
059 import com.liferay.portlet.journal.service.JournalFolderLocalServiceUtil;
060 import com.liferay.portlet.journal.util.comparator.ArticleVersionComparator;
061
062 import java.sql.Connection;
063 import java.sql.PreparedStatement;
064 import java.sql.ResultSet;
065
066 import java.util.Date;
067 import java.util.List;
068 import java.util.regex.Pattern;
069
070 import javax.portlet.PortletPreferences;
071
072
076 public class VerifyJournal extends VerifyProcess {
077
078 public static final long DEFAULT_GROUP_ID = 14;
079
080 public static final int NUM_OF_ARTICLES = 5;
081
082 @Override
083 protected void doVerify() throws Exception {
084 verifyArticleAssets();
085 verifyArticleContents();
086 verifyArticleStructures();
087 verifyContentSearch();
088 verifyFolderAssets();
089 verifyOracleNewLine();
090 verifyPermissions();
091 verifyTree();
092 verifyURLTitle();
093 }
094
095 protected void updateContentSearch(long groupId, String portletId)
096 throws Exception {
097
098 Connection con = null;
099 PreparedStatement ps = null;
100 ResultSet rs = null;
101
102 try {
103 con = DataAccess.getUpgradeOptimizedConnection();
104
105 ps = con.prepareStatement(
106 "select preferences from PortletPreferences inner join " +
107 "Layout on PortletPreferences.plid = Layout.plid where " +
108 "groupId = ? and portletId = ?");
109
110 ps.setLong(1, groupId);
111 ps.setString(2, portletId);
112
113 rs = ps.executeQuery();
114
115 while (rs.next()) {
116 String xml = rs.getString("preferences");
117
118 PortletPreferences portletPreferences =
119 PortletPreferencesFactoryUtil.fromDefaultXML(xml);
120
121 String articleId = portletPreferences.getValue(
122 "articleId", null);
123
124 List<JournalContentSearch> contentSearches =
125 JournalContentSearchLocalServiceUtil.
126 getArticleContentSearches(groupId, articleId);
127
128 if (contentSearches.isEmpty()) {
129 continue;
130 }
131
132 JournalContentSearch contentSearch = contentSearches.get(0);
133
134 JournalContentSearchLocalServiceUtil.updateContentSearch(
135 contentSearch.getGroupId(), contentSearch.isPrivateLayout(),
136 contentSearch.getLayoutId(), contentSearch.getPortletId(),
137 articleId, true);
138 }
139 }
140 finally {
141 DataAccess.cleanUp(con, ps, rs);
142 }
143 }
144
145 protected void updateCreateAndModifiedDates() throws Exception {
146 ActionableDynamicQuery actionableDynamicQuery =
147 JournalArticleResourceLocalServiceUtil.getActionableDynamicQuery();
148
149 if (_log.isDebugEnabled()) {
150 long count = actionableDynamicQuery.performCount();
151
152 _log.debug(
153 "Processing " + count +
154 " article resources for create and modified dates");
155 }
156
157 actionableDynamicQuery.setPerformActionMethod(
158 new ActionableDynamicQuery.PerformActionMethod() {
159
160 @Override
161 public void performAction(Object object) {
162 JournalArticleResource articleResource =
163 (JournalArticleResource)object;
164
165 updateCreateDate(articleResource);
166 updateModifiedDate(articleResource);
167 }
168
169 });
170
171 actionableDynamicQuery.performActions();
172
173 if (_log.isDebugEnabled()) {
174 _log.debug("Create and modified dates verified for articles");
175 }
176 }
177
178 protected void updateCreateDate(JournalArticleResource articleResource) {
179 List<JournalArticle> articles =
180 JournalArticleLocalServiceUtil.getArticles(
181 articleResource.getGroupId(), articleResource.getArticleId(),
182 QueryUtil.ALL_POS, QueryUtil.ALL_POS,
183 new ArticleVersionComparator(true));
184
185 if (articles.size() <= 1) {
186 return;
187 }
188
189 JournalArticle firstArticle = articles.get(0);
190
191 Date createDate = firstArticle.getCreateDate();
192
193 for (JournalArticle article : articles) {
194 if (!createDate.equals(article.getCreateDate())) {
195 article.setCreateDate(createDate);
196
197 JournalArticleLocalServiceUtil.updateJournalArticle(article);
198 }
199 }
200 }
201
202 protected void updateDocumentLibraryElements(Element element) {
203 Element dynamicContentElement = element.element("dynamic-content");
204
205 String path = dynamicContentElement.getStringValue();
206
207 String[] pathArray = StringUtil.split(path, CharPool.SLASH);
208
209 if (pathArray.length != 5) {
210 return;
211 }
212
213 long groupId = GetterUtil.getLong(pathArray[2]);
214 long folderId = GetterUtil.getLong(pathArray[3]);
215 String title = HttpUtil.decodeURL(HtmlUtil.escape(pathArray[4]));
216
217 DLFileEntry dlFileEntry = DLFileEntryLocalServiceUtil.fetchFileEntry(
218 groupId, folderId, title);
219
220 if (dlFileEntry == null) {
221 return;
222 }
223
224 Node node = dynamicContentElement.node(0);
225
226 node.setText(path + StringPool.SLASH + dlFileEntry.getUuid());
227 }
228
229 protected void updateDynamicElements(JournalArticle article)
230 throws Exception {
231
232 Document document = SAXReaderUtil.read(article.getContent());
233
234 Element rootElement = document.getRootElement();
235
236 updateDynamicElements(rootElement.elements("dynamic-element"));
237
238 article.setContent(document.asXML());
239
240 JournalArticleLocalServiceUtil.updateJournalArticle(article);
241 }
242
243 protected void updateDynamicElements(List<Element> dynamicElements)
244 throws PortalException {
245
246 DDMFieldsCounter ddmFieldsCounter = new DDMFieldsCounter();
247
248 for (Element dynamicElement : dynamicElements) {
249 updateDynamicElements(dynamicElement.elements("dynamic-element"));
250
251 String name = dynamicElement.attributeValue("name");
252
253 int index = ddmFieldsCounter.get(name);
254
255 dynamicElement.addAttribute("index", String.valueOf(index));
256
257 String type = dynamicElement.attributeValue("type");
258
259 if (type.equals("image")) {
260 updateImageElement(dynamicElement, name, index);
261 }
262
263 ddmFieldsCounter.incrementKey(name);
264 }
265 }
266
267 protected void updateElement(long groupId, Element element) {
268 List<Element> dynamicElementElements = element.elements(
269 "dynamic-element");
270
271 for (Element dynamicElementElement : dynamicElementElements) {
272 updateElement(groupId, dynamicElementElement);
273 }
274
275 String type = element.attributeValue("type");
276
277 if (type.equals("document_library")) {
278 updateDocumentLibraryElements(element);
279 }
280 else if (type.equals("link_to_layout")) {
281 updateLinkToLayoutElements(groupId, element);
282 }
283 }
284
285 protected void updateImageElement(Element element, String name, int index)
286 throws PortalException {
287
288 Element dynamicContentElement = element.element("dynamic-content");
289
290 long articleImageId = GetterUtil.getLong(
291 dynamicContentElement.attributeValue("id"));
292
293 JournalArticleImage articleImage =
294 JournalArticleImageLocalServiceUtil.getArticleImage(articleImageId);
295
296 articleImage.setElName(name + StringPool.UNDERLINE + index);
297
298 JournalArticleImageLocalServiceUtil.updateJournalArticleImage(
299 articleImage);
300 }
301
302 protected void updateLinkToLayoutElements(long groupId, Element element) {
303 Element dynamicContentElement = element.element("dynamic-content");
304
305 Node node = dynamicContentElement.node(0);
306
307 String text = node.getText();
308
309 if (!text.isEmpty() && !text.endsWith(StringPool.AT + groupId)) {
310 node.setText(
311 dynamicContentElement.getStringValue() + StringPool.AT +
312 groupId);
313 }
314 }
315
316 protected void updateModifiedDate(JournalArticleResource articleResource) {
317 JournalArticle article =
318 JournalArticleLocalServiceUtil.fetchLatestArticle(
319 articleResource.getResourcePrimKey(),
320 WorkflowConstants.STATUS_APPROVED, true);
321
322 if (article == null) {
323 return;
324 }
325
326 AssetEntry assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
327 articleResource.getGroupId(), articleResource.getUuid());
328
329 if (assetEntry == null) {
330 return;
331 }
332
333 Date modifiedDate = article.getModifiedDate();
334
335 if (modifiedDate.equals(assetEntry.getModifiedDate())) {
336 return;
337 }
338
339 article.setModifiedDate(assetEntry.getModifiedDate());
340
341 JournalArticleLocalServiceUtil.updateJournalArticle(article);
342 }
343
344 protected void updateResourcePrimKey() throws PortalException {
345 ActionableDynamicQuery actionableDynamicQuery =
346 JournalArticleLocalServiceUtil.getActionableDynamicQuery();
347
348 actionableDynamicQuery.setAddCriteriaMethod(
349 new ActionableDynamicQuery.AddCriteriaMethod() {
350
351 @Override
352 public void addCriteria(DynamicQuery dynamicQuery) {
353 Property resourcePrimKey = PropertyFactoryUtil.forName(
354 "resourcePrimKey");
355
356 dynamicQuery.add(resourcePrimKey.le(0l));
357 }
358
359 }
360 );
361
362 if (_log.isDebugEnabled()) {
363 long count = actionableDynamicQuery.performCount();
364
365 _log.debug(
366 "Processing " + count +
367 " default article versions in draft mode");
368 }
369
370 actionableDynamicQuery.setPerformActionMethod(
371 new ActionableDynamicQuery.PerformActionMethod() {
372
373 @Override
374 public void performAction(Object object)
375 throws PortalException {
376
377 JournalArticle article = (JournalArticle)object;
378
379 long groupId = article.getGroupId();
380 String articleId = article.getArticleId();
381 double version = article.getVersion();
382
383 JournalArticleLocalServiceUtil.checkArticleResourcePrimKey(
384 groupId, articleId, version);
385 }
386
387 });
388
389 actionableDynamicQuery.performActions();
390 }
391
392 protected void updateURLTitle(
393 long groupId, String articleId, String urlTitle)
394 throws Exception {
395
396 String normalizedURLTitle = FriendlyURLNormalizerUtil.normalize(
397 urlTitle, _friendlyURLPattern);
398
399 if (urlTitle.equals(normalizedURLTitle)) {
400 return;
401 }
402
403 normalizedURLTitle = JournalArticleLocalServiceUtil.getUniqueUrlTitle(
404 groupId, articleId, normalizedURLTitle);
405
406 Connection con = null;
407 PreparedStatement ps = null;
408
409 try {
410 con = DataAccess.getUpgradeOptimizedConnection();
411
412 ps = con.prepareStatement(
413 "update JournalArticle set urlTitle = ? where urlTitle = ?");
414
415 ps.setString(1, normalizedURLTitle);
416 ps.setString(2, urlTitle);
417
418 ps.executeUpdate();
419 }
420 finally {
421 DataAccess.cleanUp(con, ps);
422 }
423 }
424
425 protected void verifyArticleAssets() throws Exception {
426 List<JournalArticle> journalArticles =
427 JournalArticleLocalServiceUtil.getNoAssetArticles();
428
429 if (_log.isDebugEnabled()) {
430 _log.debug(
431 "Processing " + journalArticles.size() +
432 " articles with no asset");
433 }
434
435 for (JournalArticle journalArticle : journalArticles) {
436 try {
437 JournalArticleLocalServiceUtil.updateAsset(
438 journalArticle.getUserId(), journalArticle, null, null,
439 null);
440 }
441 catch (Exception e) {
442 if (_log.isWarnEnabled()) {
443 _log.warn(
444 "Unable to update asset for article " +
445 journalArticle.getId() + ": " + e.getMessage());
446 }
447 }
448 }
449
450 ActionableDynamicQuery actionableDynamicQuery =
451 JournalArticleLocalServiceUtil.getActionableDynamicQuery();
452
453 actionableDynamicQuery.setAddCriteriaMethod(
454 new ActionableDynamicQuery.AddCriteriaMethod() {
455
456 @Override
457 public void addCriteria(DynamicQuery dynamicQuery) {
458 Property versionProperty = PropertyFactoryUtil.forName(
459 "version");
460
461 dynamicQuery.add(
462 versionProperty.eq(
463 JournalArticleConstants.VERSION_DEFAULT));
464
465 Property statusProperty = PropertyFactoryUtil.forName(
466 "status");
467
468 dynamicQuery.add(
469 statusProperty.eq(WorkflowConstants.STATUS_DRAFT));
470 }
471
472 });
473
474 if (_log.isDebugEnabled()) {
475 long count = actionableDynamicQuery.performCount();
476
477 _log.debug(
478 "Processing " + count +
479 " default article versions in draft mode");
480 }
481
482 actionableDynamicQuery.setPerformActionMethod(
483 new ActionableDynamicQuery.PerformActionMethod() {
484
485 @Override
486 public void performAction(Object object)
487 throws PortalException {
488
489 JournalArticle article = (JournalArticle)object;
490
491 AssetEntry assetEntry =
492 AssetEntryLocalServiceUtil.fetchEntry(
493 JournalArticle.class.getName(),
494 article.getResourcePrimKey());
495
496 AssetEntryLocalServiceUtil.updateEntry(
497 assetEntry.getClassName(), assetEntry.getClassPK(),
498 null, assetEntry.isVisible());
499 }
500
501 });
502
503 actionableDynamicQuery.performActions();
504
505 if (_log.isDebugEnabled()) {
506 _log.debug("Assets verified for articles");
507 }
508
509 updateCreateAndModifiedDates();
510 updateResourcePrimKey();
511 }
512
513 protected void verifyArticleContents() throws Exception {
514 Connection con = null;
515 PreparedStatement ps = null;
516 ResultSet rs = null;
517
518 try {
519 con = DataAccess.getUpgradeOptimizedConnection();
520
521 ps = con.prepareStatement(
522 "select id_ from JournalArticle where (content like " +
523 "'%document_library%' or content like '%link_to_layout%')" +
524 " and DDMStructureKey != ''");
525
526 rs = ps.executeQuery();
527
528 while (rs.next()) {
529 long id = rs.getLong("id_");
530
531 JournalArticle article =
532 JournalArticleLocalServiceUtil.getArticle(id);
533
534 Document document = SAXReaderUtil.read(article.getContent());
535
536 Element rootElement = document.getRootElement();
537
538 for (Element element : rootElement.elements()) {
539 updateElement(article.getGroupId(), element);
540 }
541
542 article.setContent(document.asXML());
543
544 JournalArticleLocalServiceUtil.updateJournalArticle(article);
545 }
546 }
547 finally {
548 DataAccess.cleanUp(con, ps, rs);
549 }
550 }
551
552 protected void verifyArticleStructures() throws PortalException {
553 ActionableDynamicQuery actionableDynamicQuery =
554 JournalArticleLocalServiceUtil.getActionableDynamicQuery();
555
556 if (_log.isDebugEnabled()) {
557 long count = actionableDynamicQuery.performCount();
558
559 _log.debug(
560 "Processing " + count + " articles for invalid structures " +
561 "and dynamic elements");
562 }
563
564 actionableDynamicQuery.setPerformActionMethod(
565 new ActionableDynamicQuery.PerformActionMethod() {
566
567 @Override
568 public void performAction(Object object) {
569 JournalArticle article = (JournalArticle)object;
570
571 try {
572 JournalArticleLocalServiceUtil.checkStructure(
573 article.getGroupId(), article.getArticleId(),
574 article.getVersion());
575 }
576 catch (NoSuchStructureException nsse) {
577 if (_log.isWarnEnabled()) {
578 _log.warn(
579 "Removing reference to missing structure for " +
580 "article " + article.getId());
581 }
582
583 article.setDDMStructureKey(StringPool.BLANK);
584 article.setDDMTemplateKey(StringPool.BLANK);
585
586 JournalArticleLocalServiceUtil.updateJournalArticle(
587 article);
588 }
589 catch (Exception e) {
590 _log.error(
591 "Unable to check the structure for article " +
592 article.getId(),
593 e);
594 }
595
596 try {
597 updateDynamicElements(article);
598 }
599 catch (Exception e) {
600 _log.error(
601 "Unable to update content for article " +
602 article.getId(),
603 e);
604 }
605 }
606
607 });
608
609 actionableDynamicQuery.performActions();
610 }
611
612 protected void verifyContentSearch() throws Exception {
613 Connection con = null;
614 PreparedStatement ps = null;
615 ResultSet rs = null;
616
617 try {
618 con = DataAccess.getUpgradeOptimizedConnection();
619
620 ps = con.prepareStatement(
621 "select groupId, portletId from JournalContentSearch group " +
622 "by groupId, portletId having count(groupId) > 1 and " +
623 "count(portletId) > 1");
624
625 rs = ps.executeQuery();
626
627 while (rs.next()) {
628 long groupId = rs.getLong("groupId");
629 String portletId = rs.getString("portletId");
630
631 updateContentSearch(groupId, portletId);
632 }
633 }
634 finally {
635 DataAccess.cleanUp(con, ps, rs);
636 }
637 }
638
639 protected void verifyFolderAssets() throws Exception {
640 List<JournalFolder> folders =
641 JournalFolderLocalServiceUtil.getNoAssetFolders();
642
643 if (_log.isDebugEnabled()) {
644 _log.debug(
645 "Processing " + folders.size() + " folders with no asset");
646 }
647
648 for (JournalFolder folder : folders) {
649 try {
650 JournalFolderLocalServiceUtil.updateAsset(
651 folder.getUserId(), folder, null, null, null);
652 }
653 catch (Exception e) {
654 if (_log.isWarnEnabled()) {
655 _log.warn(
656 "Unable to update asset for folder " +
657 folder.getFolderId() + ": " + e.getMessage());
658 }
659 }
660 }
661
662 if (_log.isDebugEnabled()) {
663 _log.debug("Assets verified for folders");
664 }
665 }
666
667 protected void verifyOracleNewLine() throws Exception {
668 DB db = DBFactoryUtil.getDB();
669
670 String dbType = db.getType();
671
672 if (!dbType.equals(DB.TYPE_ORACLE)) {
673 return;
674 }
675
676
677
678
679
680
681
682 boolean checkNewLine = false;
683
684 List<JournalArticle> articles =
685 JournalArticleLocalServiceUtil.getArticles(
686 DEFAULT_GROUP_ID, 0, NUM_OF_ARTICLES);
687
688 for (JournalArticle article : articles) {
689 String content = article.getContent();
690
691 if ((content != null) && content.contains("\\n")) {
692 articles = JournalArticleLocalServiceUtil.getArticles(
693 DEFAULT_GROUP_ID);
694
695 for (int j = 0; j < articles.size(); j++) {
696 article = articles.get(j);
697
698 JournalArticleLocalServiceUtil.checkNewLine(
699 article.getGroupId(), article.getArticleId(),
700 article.getVersion());
701 }
702
703 checkNewLine = true;
704
705 break;
706 }
707 }
708
709
710
711 if (!checkNewLine) {
712 if (_log.isInfoEnabled()) {
713 _log.info("Do not fix oracle new line");
714 }
715
716 return;
717 }
718 else {
719 if (_log.isInfoEnabled()) {
720 _log.info("Fix oracle new line");
721 }
722 }
723 }
724
725 protected void verifyPermissions() throws PortalException {
726 List<JournalArticle> articles =
727 JournalArticleLocalServiceUtil.getNoPermissionArticles();
728
729 for (JournalArticle article : articles) {
730 ResourceLocalServiceUtil.addResources(
731 article.getCompanyId(), 0, 0, JournalArticle.class.getName(),
732 article.getResourcePrimKey(), false, false, false);
733 }
734 }
735
736 protected void verifyTree() throws Exception {
737 long[] companyIds = PortalInstances.getCompanyIdsBySQL();
738
739 for (long companyId : companyIds) {
740 JournalFolderLocalServiceUtil.rebuildTree(companyId);
741 }
742 }
743
744 protected void verifyURLTitle() throws Exception {
745 Connection con = null;
746 PreparedStatement ps = null;
747 ResultSet rs = null;
748
749 try {
750 con = DataAccess.getUpgradeOptimizedConnection();
751
752 ps = con.prepareStatement(
753 "select distinct groupId, articleId, urlTitle from " +
754 "JournalArticle");
755
756 rs = ps.executeQuery();
757
758 while (rs.next()) {
759 long groupId = rs.getLong("groupId");
760 String articleId = rs.getString("articleId");
761 String urlTitle = GetterUtil.getString(
762 rs.getString("urlTitle"));
763
764 updateURLTitle(groupId, articleId, urlTitle);
765 }
766 }
767 finally {
768 DataAccess.cleanUp(con, ps, rs);
769 }
770 }
771
772 private static Log _log = LogFactoryUtil.getLog(VerifyJournal.class);
773
774 private static Pattern _friendlyURLPattern = Pattern.compile("[^a-z0-9_-]");
775
776 }