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.repository.model.FileEntry;
029 import com.liferay.portal.kernel.util.CharPool;
030 import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
031 import com.liferay.portal.kernel.util.GetterUtil;
032 import com.liferay.portal.kernel.util.HtmlUtil;
033 import com.liferay.portal.kernel.util.HttpUtil;
034 import com.liferay.portal.kernel.util.StringPool;
035 import com.liferay.portal.kernel.util.StringUtil;
036 import com.liferay.portal.kernel.workflow.WorkflowConstants;
037 import com.liferay.portal.kernel.xml.Document;
038 import com.liferay.portal.kernel.xml.Element;
039 import com.liferay.portal.kernel.xml.Node;
040 import com.liferay.portal.kernel.xml.SAXReaderUtil;
041 import com.liferay.portal.service.ResourceLocalServiceUtil;
042 import com.liferay.portal.util.PortalInstances;
043 import com.liferay.portlet.PortletPreferencesFactoryUtil;
044 import com.liferay.portlet.asset.model.AssetEntry;
045 import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
046 import com.liferay.portlet.documentlibrary.service.DLAppLocalServiceUtil;
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 try {
218 FileEntry fileEntry = DLAppLocalServiceUtil.getFileEntry(
219 groupId, folderId, title);
220
221 Node node = dynamicContentElement.node(0);
222
223 node.setText(path + StringPool.SLASH + fileEntry.getUuid());
224 }
225 catch (PortalException pe) {
226 }
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 Element dynamicContentElement = element.element("dynamic-content");
287
288 long articleImageId = GetterUtil.getLong(
289 dynamicContentElement.attributeValue("id"));
290
291 JournalArticleImage articleImage =
292 JournalArticleImageLocalServiceUtil.fetchJournalArticleImage(
293 articleImageId);
294
295 if (articleImage == null) {
296 return;
297 }
298
299 articleImage.setElName(name + StringPool.UNDERLINE + index);
300
301 JournalArticleImageLocalServiceUtil.updateJournalArticleImage(
302 articleImage);
303 }
304
305 protected void updateLinkToLayoutElements(long groupId, Element element) {
306 Element dynamicContentElement = element.element("dynamic-content");
307
308 Node node = dynamicContentElement.node(0);
309
310 String text = node.getText();
311
312 if (!text.isEmpty() && !text.endsWith(StringPool.AT + groupId)) {
313 node.setText(
314 dynamicContentElement.getStringValue() + StringPool.AT +
315 groupId);
316 }
317 }
318
319 protected void updateModifiedDate(JournalArticleResource articleResource) {
320 JournalArticle article =
321 JournalArticleLocalServiceUtil.fetchLatestArticle(
322 articleResource.getResourcePrimKey(),
323 WorkflowConstants.STATUS_APPROVED, true);
324
325 if (article == null) {
326 return;
327 }
328
329 AssetEntry assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
330 articleResource.getGroupId(), articleResource.getUuid());
331
332 if (assetEntry == null) {
333 return;
334 }
335
336 Date modifiedDate = article.getModifiedDate();
337
338 if (modifiedDate.equals(assetEntry.getModifiedDate())) {
339 return;
340 }
341
342 article.setModifiedDate(assetEntry.getModifiedDate());
343
344 JournalArticleLocalServiceUtil.updateJournalArticle(article);
345 }
346
347 protected void updateResourcePrimKey() throws PortalException {
348 ActionableDynamicQuery actionableDynamicQuery =
349 JournalArticleLocalServiceUtil.getActionableDynamicQuery();
350
351 actionableDynamicQuery.setAddCriteriaMethod(
352 new ActionableDynamicQuery.AddCriteriaMethod() {
353
354 @Override
355 public void addCriteria(DynamicQuery dynamicQuery) {
356 Property resourcePrimKey = PropertyFactoryUtil.forName(
357 "resourcePrimKey");
358
359 dynamicQuery.add(resourcePrimKey.le(0l));
360 }
361
362 }
363 );
364
365 if (_log.isDebugEnabled()) {
366 long count = actionableDynamicQuery.performCount();
367
368 _log.debug(
369 "Processing " + count +
370 " default article versions in draft mode");
371 }
372
373 actionableDynamicQuery.setPerformActionMethod(
374 new ActionableDynamicQuery.PerformActionMethod() {
375
376 @Override
377 public void performAction(Object object)
378 throws PortalException {
379
380 JournalArticle article = (JournalArticle)object;
381
382 long groupId = article.getGroupId();
383 String articleId = article.getArticleId();
384 double version = article.getVersion();
385
386 JournalArticleLocalServiceUtil.checkArticleResourcePrimKey(
387 groupId, articleId, version);
388 }
389
390 });
391
392 actionableDynamicQuery.performActions();
393 }
394
395 protected void updateURLTitle(
396 long groupId, String articleId, String urlTitle)
397 throws Exception {
398
399 String normalizedURLTitle = FriendlyURLNormalizerUtil.normalize(
400 urlTitle, _friendlyURLPattern);
401
402 if (urlTitle.equals(normalizedURLTitle)) {
403 return;
404 }
405
406 normalizedURLTitle = JournalArticleLocalServiceUtil.getUniqueUrlTitle(
407 groupId, articleId, normalizedURLTitle);
408
409 Connection con = null;
410 PreparedStatement ps = null;
411
412 try {
413 con = DataAccess.getUpgradeOptimizedConnection();
414
415 ps = con.prepareStatement(
416 "update JournalArticle set urlTitle = ? where urlTitle = ?");
417
418 ps.setString(1, normalizedURLTitle);
419 ps.setString(2, urlTitle);
420
421 ps.executeUpdate();
422 }
423 finally {
424 DataAccess.cleanUp(con, ps);
425 }
426 }
427
428 protected void verifyArticleAssets() throws Exception {
429 List<JournalArticle> journalArticles =
430 JournalArticleLocalServiceUtil.getNoAssetArticles();
431
432 if (_log.isDebugEnabled()) {
433 _log.debug(
434 "Processing " + journalArticles.size() +
435 " articles with no asset");
436 }
437
438 for (JournalArticle journalArticle : journalArticles) {
439 try {
440 JournalArticleLocalServiceUtil.updateAsset(
441 journalArticle.getUserId(), journalArticle, null, null,
442 null);
443 }
444 catch (Exception e) {
445 if (_log.isWarnEnabled()) {
446 _log.warn(
447 "Unable to update asset for article " +
448 journalArticle.getId() + ": " + e.getMessage());
449 }
450 }
451 }
452
453 ActionableDynamicQuery actionableDynamicQuery =
454 JournalArticleLocalServiceUtil.getActionableDynamicQuery();
455
456 actionableDynamicQuery.setAddCriteriaMethod(
457 new ActionableDynamicQuery.AddCriteriaMethod() {
458
459 @Override
460 public void addCriteria(DynamicQuery dynamicQuery) {
461 Property versionProperty = PropertyFactoryUtil.forName(
462 "version");
463
464 dynamicQuery.add(
465 versionProperty.eq(
466 JournalArticleConstants.VERSION_DEFAULT));
467
468 Property statusProperty = PropertyFactoryUtil.forName(
469 "status");
470
471 dynamicQuery.add(
472 statusProperty.eq(WorkflowConstants.STATUS_DRAFT));
473 }
474
475 });
476
477 if (_log.isDebugEnabled()) {
478 long count = actionableDynamicQuery.performCount();
479
480 _log.debug(
481 "Processing " + count +
482 " default article versions in draft mode");
483 }
484
485 actionableDynamicQuery.setPerformActionMethod(
486 new ActionableDynamicQuery.PerformActionMethod() {
487
488 @Override
489 public void performAction(Object object)
490 throws PortalException {
491
492 JournalArticle article = (JournalArticle)object;
493
494 AssetEntry assetEntry =
495 AssetEntryLocalServiceUtil.fetchEntry(
496 JournalArticle.class.getName(),
497 article.getResourcePrimKey());
498
499 AssetEntryLocalServiceUtil.updateEntry(
500 assetEntry.getClassName(), assetEntry.getClassPK(),
501 null, assetEntry.isVisible());
502 }
503
504 });
505
506 actionableDynamicQuery.performActions();
507
508 if (_log.isDebugEnabled()) {
509 _log.debug("Assets verified for articles");
510 }
511
512 updateCreateAndModifiedDates();
513 updateResourcePrimKey();
514 }
515
516 protected void verifyArticleContents() throws Exception {
517 Connection con = null;
518 PreparedStatement ps = null;
519 ResultSet rs = null;
520
521 try {
522 con = DataAccess.getUpgradeOptimizedConnection();
523
524 ps = con.prepareStatement(
525 "select id_ from JournalArticle where (content like " +
526 "'%document_library%' or content like '%link_to_layout%')" +
527 " and DDMStructureKey != ''");
528
529 rs = ps.executeQuery();
530
531 while (rs.next()) {
532 long id = rs.getLong("id_");
533
534 JournalArticle article =
535 JournalArticleLocalServiceUtil.getArticle(id);
536
537 try {
538 Document document = SAXReaderUtil.read(
539 article.getContent());
540
541 Element rootElement = document.getRootElement();
542
543 for (Element element : rootElement.elements()) {
544 updateElement(article.getGroupId(), element);
545 }
546
547 article.setContent(document.asXML());
548
549 JournalArticleLocalServiceUtil.updateJournalArticle(
550 article);
551 }
552 catch (Exception e) {
553 _log.error(
554 "Unable to update content for article " +
555 article.getId(),
556 e);
557 }
558 }
559 }
560 finally {
561 DataAccess.cleanUp(con, ps, rs);
562 }
563 }
564
565 protected void verifyArticleStructures() throws PortalException {
566 ActionableDynamicQuery actionableDynamicQuery =
567 JournalArticleLocalServiceUtil.getActionableDynamicQuery();
568
569 if (_log.isDebugEnabled()) {
570 long count = actionableDynamicQuery.performCount();
571
572 _log.debug(
573 "Processing " + count + " articles for invalid structures " +
574 "and dynamic elements");
575 }
576
577 actionableDynamicQuery.setPerformActionMethod(
578 new ActionableDynamicQuery.PerformActionMethod() {
579
580 @Override
581 public void performAction(Object object) {
582 JournalArticle article = (JournalArticle)object;
583
584 try {
585 JournalArticleLocalServiceUtil.checkStructure(
586 article.getGroupId(), article.getArticleId(),
587 article.getVersion());
588 }
589 catch (NoSuchStructureException nsse) {
590 if (_log.isWarnEnabled()) {
591 _log.warn(
592 "Removing reference to missing structure for " +
593 "article " + article.getId());
594 }
595
596 article.setDDMStructureKey(StringPool.BLANK);
597 article.setDDMTemplateKey(StringPool.BLANK);
598
599 JournalArticleLocalServiceUtil.updateJournalArticle(
600 article);
601 }
602 catch (Exception e) {
603 _log.error(
604 "Unable to check the structure for article " +
605 article.getId(),
606 e);
607 }
608
609 try {
610 updateDynamicElements(article);
611 }
612 catch (Exception e) {
613 _log.error(
614 "Unable to update content for article " +
615 article.getId(),
616 e);
617 }
618 }
619
620 });
621
622 actionableDynamicQuery.performActions();
623 }
624
625 protected void verifyContentSearch() throws Exception {
626 Connection con = null;
627 PreparedStatement ps = null;
628 ResultSet rs = null;
629
630 try {
631 con = DataAccess.getUpgradeOptimizedConnection();
632
633 ps = con.prepareStatement(
634 "select groupId, portletId from JournalContentSearch group " +
635 "by groupId, portletId having count(groupId) > 1 and " +
636 "count(portletId) > 1");
637
638 rs = ps.executeQuery();
639
640 while (rs.next()) {
641 long groupId = rs.getLong("groupId");
642 String portletId = rs.getString("portletId");
643
644 updateContentSearch(groupId, portletId);
645 }
646 }
647 finally {
648 DataAccess.cleanUp(con, ps, rs);
649 }
650 }
651
652 protected void verifyFolderAssets() throws Exception {
653 List<JournalFolder> folders =
654 JournalFolderLocalServiceUtil.getNoAssetFolders();
655
656 if (_log.isDebugEnabled()) {
657 _log.debug(
658 "Processing " + folders.size() + " folders with no asset");
659 }
660
661 for (JournalFolder folder : folders) {
662 try {
663 JournalFolderLocalServiceUtil.updateAsset(
664 folder.getUserId(), folder, null, null, null);
665 }
666 catch (Exception e) {
667 if (_log.isWarnEnabled()) {
668 _log.warn(
669 "Unable to update asset for folder " +
670 folder.getFolderId() + ": " + e.getMessage());
671 }
672 }
673 }
674
675 if (_log.isDebugEnabled()) {
676 _log.debug("Assets verified for folders");
677 }
678 }
679
680 protected void verifyOracleNewLine() throws Exception {
681 DB db = DBFactoryUtil.getDB();
682
683 String dbType = db.getType();
684
685 if (!dbType.equals(DB.TYPE_ORACLE)) {
686 return;
687 }
688
689
690
691
692
693
694
695 boolean checkNewLine = false;
696
697 List<JournalArticle> articles =
698 JournalArticleLocalServiceUtil.getArticles(
699 DEFAULT_GROUP_ID, 0, NUM_OF_ARTICLES);
700
701 for (JournalArticle article : articles) {
702 String content = article.getContent();
703
704 if ((content != null) && content.contains("\\n")) {
705 articles = JournalArticleLocalServiceUtil.getArticles(
706 DEFAULT_GROUP_ID);
707
708 for (int j = 0; j < articles.size(); j++) {
709 article = articles.get(j);
710
711 JournalArticleLocalServiceUtil.checkNewLine(
712 article.getGroupId(), article.getArticleId(),
713 article.getVersion());
714 }
715
716 checkNewLine = true;
717
718 break;
719 }
720 }
721
722
723
724 if (!checkNewLine) {
725 if (_log.isInfoEnabled()) {
726 _log.info("Do not fix oracle new line");
727 }
728
729 return;
730 }
731 else {
732 if (_log.isInfoEnabled()) {
733 _log.info("Fix oracle new line");
734 }
735 }
736 }
737
738 protected void verifyPermissions() throws PortalException {
739 List<JournalArticle> articles =
740 JournalArticleLocalServiceUtil.getNoPermissionArticles();
741
742 for (JournalArticle article : articles) {
743 ResourceLocalServiceUtil.addResources(
744 article.getCompanyId(), 0, 0, JournalArticle.class.getName(),
745 article.getResourcePrimKey(), false, false, false);
746 }
747 }
748
749 protected void verifyTree() throws Exception {
750 long[] companyIds = PortalInstances.getCompanyIdsBySQL();
751
752 for (long companyId : companyIds) {
753 JournalFolderLocalServiceUtil.rebuildTree(companyId);
754 }
755 }
756
757 protected void verifyURLTitle() throws Exception {
758 Connection con = null;
759 PreparedStatement ps = null;
760 ResultSet rs = null;
761
762 try {
763 con = DataAccess.getUpgradeOptimizedConnection();
764
765 ps = con.prepareStatement(
766 "select distinct groupId, articleId, urlTitle from " +
767 "JournalArticle");
768
769 rs = ps.executeQuery();
770
771 while (rs.next()) {
772 long groupId = rs.getLong("groupId");
773 String articleId = rs.getString("articleId");
774 String urlTitle = GetterUtil.getString(
775 rs.getString("urlTitle"));
776
777 updateURLTitle(groupId, articleId, urlTitle);
778 }
779 }
780 finally {
781 DataAccess.cleanUp(con, ps, rs);
782 }
783 }
784
785 private static final Log _log = LogFactoryUtil.getLog(VerifyJournal.class);
786
787 private static final Pattern _friendlyURLPattern = Pattern.compile(
788 "[^a-z0-9_-]");
789
790 }