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