001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.softwarecatalog.service.impl;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.plugin.Version;
019    import com.liferay.portal.kernel.search.Indexable;
020    import com.liferay.portal.kernel.search.IndexableType;
021    import com.liferay.portal.kernel.util.OrderByComparator;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Time;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.kernel.workflow.WorkflowConstants;
027    import com.liferay.portal.kernel.xml.Document;
028    import com.liferay.portal.kernel.xml.Element;
029    import com.liferay.portal.kernel.xml.SAXReaderUtil;
030    import com.liferay.portal.model.ResourceConstants;
031    import com.liferay.portal.model.User;
032    import com.liferay.portal.plugin.ModuleId;
033    import com.liferay.portal.service.ServiceContext;
034    import com.liferay.portal.service.permission.ModelPermissions;
035    import com.liferay.portal.util.PropsValues;
036    import com.liferay.portal.webserver.WebServerServletTokenUtil;
037    import com.liferay.portlet.softwarecatalog.DuplicateProductEntryModuleIdException;
038    import com.liferay.portlet.softwarecatalog.ProductEntryAuthorException;
039    import com.liferay.portlet.softwarecatalog.ProductEntryLicenseException;
040    import com.liferay.portlet.softwarecatalog.ProductEntryNameException;
041    import com.liferay.portlet.softwarecatalog.ProductEntryPageURLException;
042    import com.liferay.portlet.softwarecatalog.ProductEntryScreenshotsException;
043    import com.liferay.portlet.softwarecatalog.ProductEntryShortDescriptionException;
044    import com.liferay.portlet.softwarecatalog.ProductEntryTypeException;
045    import com.liferay.portlet.softwarecatalog.model.SCFrameworkVersion;
046    import com.liferay.portlet.softwarecatalog.model.SCLicense;
047    import com.liferay.portlet.softwarecatalog.model.SCProductEntry;
048    import com.liferay.portlet.softwarecatalog.model.SCProductScreenshot;
049    import com.liferay.portlet.softwarecatalog.model.SCProductVersion;
050    import com.liferay.portlet.softwarecatalog.service.base.SCProductEntryLocalServiceBaseImpl;
051    import com.liferay.util.xml.DocUtil;
052    
053    import java.util.Date;
054    import java.util.List;
055    import java.util.Map;
056    import java.util.Properties;
057    
058    /**
059     * @author Jorge Ferrer
060     * @author Brian Wing Shun Chan
061     * @author Raymond Aug??
062     */
063    public class SCProductEntryLocalServiceImpl
064            extends SCProductEntryLocalServiceBaseImpl {
065    
066            @Indexable(type = IndexableType.REINDEX)
067            @Override
068            public SCProductEntry addProductEntry(
069                            long userId, String name, String type, String tags,
070                            String shortDescription, String longDescription, String pageURL,
071                            String author, String repoGroupId, String repoArtifactId,
072                            long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages,
073                            ServiceContext serviceContext)
074                    throws PortalException {
075    
076                    // Product entry
077    
078                    User user = userPersistence.findByPrimaryKey(userId);
079                    long groupId = serviceContext.getScopeGroupId();
080                    tags = getTags(tags);
081                    repoGroupId = StringUtil.toLowerCase(repoGroupId.trim());
082                    repoArtifactId = StringUtil.toLowerCase(repoArtifactId.trim());
083    
084                    validate(
085                            0, name, type, shortDescription, pageURL, author, repoGroupId,
086                            repoArtifactId, licenseIds, thumbnails, fullImages);
087    
088                    long productEntryId = counterLocalService.increment();
089    
090                    SCProductEntry productEntry = scProductEntryPersistence.create(
091                            productEntryId);
092    
093                    productEntry.setGroupId(groupId);
094                    productEntry.setCompanyId(user.getCompanyId());
095                    productEntry.setUserId(user.getUserId());
096                    productEntry.setUserName(user.getFullName());
097                    productEntry.setName(name);
098                    productEntry.setType(type);
099                    productEntry.setTags(tags);
100                    productEntry.setShortDescription(shortDescription);
101                    productEntry.setLongDescription(longDescription);
102                    productEntry.setPageURL(pageURL);
103                    productEntry.setAuthor(author);
104                    productEntry.setRepoGroupId(repoGroupId);
105                    productEntry.setRepoArtifactId(repoArtifactId);
106    
107                    scProductEntryPersistence.update(productEntry);
108    
109                    // Resources
110    
111                    if (serviceContext.isAddGroupPermissions() ||
112                            serviceContext.isAddGuestPermissions()) {
113    
114                            addProductEntryResources(
115                                    productEntry, serviceContext.isAddGroupPermissions(),
116                                    serviceContext.isAddGuestPermissions());
117                    }
118                    else {
119                            addProductEntryResources(
120                                    productEntry, serviceContext.getModelPermissions());
121                    }
122    
123                    // Licenses
124    
125                    scProductEntryPersistence.setSCLicenses(productEntryId, licenseIds);
126    
127                    // Product screenshots
128    
129                    saveProductScreenshots(productEntry, thumbnails, fullImages);
130    
131                    // Message boards
132    
133                    if (PropsValues.SC_PRODUCT_COMMENTS_ENABLED) {
134                            mbMessageLocalService.addDiscussionMessage(
135                                    userId, productEntry.getUserName(), groupId,
136                                    SCProductEntry.class.getName(), productEntryId,
137                                    WorkflowConstants.ACTION_PUBLISH);
138                    }
139    
140                    return productEntry;
141            }
142    
143            @Override
144            public void addProductEntryResources(
145                            long productEntryId, boolean addGroupPermissions,
146                            boolean addGuestPermissions)
147                    throws PortalException {
148    
149                    SCProductEntry productEntry =
150                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
151    
152                    addProductEntryResources(
153                            productEntry, addGroupPermissions, addGuestPermissions);
154            }
155    
156            @Override
157            public void addProductEntryResources(
158                            long productEntryId, ModelPermissions modelPermissions)
159                    throws PortalException {
160    
161                    SCProductEntry productEntry =
162                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
163    
164                    addProductEntryResources(productEntry, modelPermissions);
165            }
166    
167            @Override
168            public void addProductEntryResources(
169                            SCProductEntry productEntry, boolean addGroupPermissions,
170                            boolean addGuestPermissions)
171                    throws PortalException {
172    
173                    resourceLocalService.addResources(
174                            productEntry.getCompanyId(), productEntry.getGroupId(),
175                            productEntry.getUserId(), SCProductEntry.class.getName(),
176                            productEntry.getProductEntryId(), false, addGroupPermissions,
177                            addGuestPermissions);
178            }
179    
180            @Override
181            public void addProductEntryResources(
182                            SCProductEntry productEntry, ModelPermissions modelPermissions)
183                    throws PortalException {
184    
185                    resourceLocalService.addModelResources(
186                            productEntry.getCompanyId(), productEntry.getGroupId(),
187                            productEntry.getUserId(), SCProductEntry.class.getName(),
188                            productEntry.getProductEntryId(), modelPermissions);
189            }
190    
191            @Override
192            public void deleteProductEntries(long groupId) throws PortalException {
193                    List<SCProductEntry> productEntries =
194                            scProductEntryPersistence.findByGroupId(groupId);
195    
196                    for (SCProductEntry productEntry : productEntries) {
197                            scProductEntryLocalService.deleteProductEntry(productEntry);
198                    }
199            }
200    
201            @Indexable(type = IndexableType.DELETE)
202            @Override
203            public SCProductEntry deleteProductEntry(long productEntryId)
204                    throws PortalException {
205    
206                    SCProductEntry productEntry =
207                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
208    
209                    return deleteProductEntry(productEntry);
210            }
211    
212            @Indexable(type = IndexableType.DELETE)
213            @Override
214            public SCProductEntry deleteProductEntry(SCProductEntry productEntry)
215                    throws PortalException {
216    
217                    // Product entry
218    
219                    scProductEntryPersistence.remove(productEntry);
220    
221                    // Resources
222    
223                    resourceLocalService.deleteResource(
224                            productEntry.getCompanyId(), SCProductEntry.class.getName(),
225                            ResourceConstants.SCOPE_INDIVIDUAL,
226                            productEntry.getProductEntryId());
227    
228                    // Subscriptions
229    
230                    subscriptionLocalService.deleteSubscriptions(
231                            productEntry.getCompanyId(), SCProductEntry.class.getName(),
232                            productEntry.getProductEntryId());
233    
234                    // Product screenshots
235    
236                    scProductScreenshotLocalService.deleteProductScreenshots(
237                            productEntry.getProductEntryId());
238    
239                    // Product versions
240    
241                    scProductVersionLocalService.deleteProductVersions(
242                            productEntry.getProductEntryId());
243    
244                    // Message boards
245    
246                    mbMessageLocalService.deleteDiscussionMessages(
247                            SCProductEntry.class.getName(), productEntry.getProductEntryId());
248    
249                    // Ratings
250    
251                    ratingsStatsLocalService.deleteStats(
252                            SCProductEntry.class.getName(), productEntry.getProductEntryId());
253    
254                    return productEntry;
255            }
256    
257            @Override
258            public List<SCProductEntry> getCompanyProductEntries(
259                    long companyId, int start, int end) {
260    
261                    return scProductEntryPersistence.findByCompanyId(companyId, start, end);
262            }
263    
264            @Override
265            public int getCompanyProductEntriesCount(long companyId) {
266                    return scProductEntryPersistence.countByCompanyId(companyId);
267            }
268    
269            @Override
270            public List<SCProductEntry> getProductEntries(
271                    long groupId, int start, int end) {
272    
273                    return scProductEntryPersistence.findByGroupId(groupId, start, end);
274            }
275    
276            @Override
277            public List<SCProductEntry> getProductEntries(
278                    long groupId, int start, int end,
279                    OrderByComparator<SCProductEntry> obc) {
280    
281                    return scProductEntryPersistence.findByGroupId(
282                            groupId, start, end, obc);
283            }
284    
285            @Override
286            public List<SCProductEntry> getProductEntries(
287                    long groupId, long userId, int start, int end) {
288    
289                    return scProductEntryPersistence.findByG_U(groupId, userId, start, end);
290            }
291    
292            @Override
293            public List<SCProductEntry> getProductEntries(
294                    long groupId, long userId, int start, int end,
295                    OrderByComparator<SCProductEntry> obc) {
296    
297                    return scProductEntryPersistence.findByG_U(
298                            groupId, userId, start, end, obc);
299            }
300    
301            @Override
302            public int getProductEntriesCount(long groupId) {
303                    return scProductEntryPersistence.countByGroupId(groupId);
304            }
305    
306            @Override
307            public int getProductEntriesCount(long groupId, long userId) {
308                    return scProductEntryPersistence.countByG_U(groupId, userId);
309            }
310    
311            @Override
312            public SCProductEntry getProductEntry(long productEntryId)
313                    throws PortalException {
314    
315                    return scProductEntryPersistence.findByPrimaryKey(productEntryId);
316            }
317    
318            @Override
319            public String getRepositoryXML(
320                    long groupId, String baseImageURL, Date oldestDate,
321                    int maxNumOfVersions, Properties repoSettings) {
322    
323                    return getRepositoryXML(
324                            groupId, null, baseImageURL, oldestDate, maxNumOfVersions,
325                            repoSettings);
326            }
327    
328            @Override
329            public String getRepositoryXML(
330                    long groupId, String version, String baseImageURL, Date oldestDate,
331                    int maxNumOfVersions, Properties repoSettings) {
332    
333                    Document doc = SAXReaderUtil.createDocument();
334    
335                    doc.setXMLEncoding(StringPool.UTF8);
336    
337                    Element root = doc.addElement("plugin-repository");
338    
339                    Element settingsEl = root.addElement("settings");
340    
341                    populateSettingsElement(settingsEl, repoSettings);
342    
343                    List<SCProductEntry> productEntries =
344                            scProductEntryPersistence.findByGroupId(groupId);
345    
346                    for (SCProductEntry productEntry : productEntries) {
347                            if (Validator.isNull(productEntry.getRepoGroupId()) ||
348                                    Validator.isNull(productEntry.getRepoArtifactId())) {
349    
350                                    continue;
351                            }
352    
353                            List<SCProductVersion> productVersions =
354                                    scProductVersionPersistence.findByProductEntryId(
355                                            productEntry.getProductEntryId());
356    
357                            for (int i = 0; i < productVersions.size(); i++) {
358                                    SCProductVersion productVersion = productVersions.get(i);
359    
360                                    if ((maxNumOfVersions > 0) && (maxNumOfVersions < (i + 1))) {
361                                            break;
362                                    }
363    
364                                    if (!productVersion.isRepoStoreArtifact()) {
365                                            continue;
366                                    }
367    
368                                    if ((oldestDate != null) &&
369                                            oldestDate.after(productVersion.getModifiedDate())) {
370    
371                                            continue;
372                                    }
373    
374                                    if (Validator.isNotNull(version) &&
375                                            !isVersionSupported(
376                                                    version, productVersion.getFrameworkVersions())) {
377    
378                                            continue;
379                                    }
380    
381                                    Element el = root.addElement("plugin-package");
382    
383                                    populatePluginPackageElement(
384                                            el, productEntry, productVersion, baseImageURL);
385                            }
386                    }
387    
388                    return doc.asXML();
389            }
390    
391            @Indexable(type = IndexableType.REINDEX)
392            @Override
393            public SCProductEntry updateProductEntry(
394                            long productEntryId, String name, String type, String tags,
395                            String shortDescription, String longDescription, String pageURL,
396                            String author, String repoGroupId, String repoArtifactId,
397                            long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages)
398                    throws PortalException {
399    
400                    // Product entry
401    
402                    tags = getTags(tags);
403                    repoGroupId = StringUtil.toLowerCase(repoGroupId.trim());
404                    repoArtifactId = StringUtil.toLowerCase(repoArtifactId.trim());
405    
406                    validate(
407                            productEntryId, name, type, shortDescription, pageURL, author,
408                            repoGroupId, repoArtifactId, licenseIds, thumbnails, fullImages);
409    
410                    SCProductEntry productEntry =
411                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
412    
413                    productEntry.setName(name);
414                    productEntry.setType(type);
415                    productEntry.setTags(tags);
416                    productEntry.setShortDescription(shortDescription);
417                    productEntry.setLongDescription(longDescription);
418                    productEntry.setPageURL(pageURL);
419                    productEntry.setAuthor(author);
420                    productEntry.setRepoGroupId(repoGroupId);
421                    productEntry.setRepoArtifactId(repoArtifactId);
422    
423                    scProductEntryPersistence.update(productEntry);
424    
425                    // Licenses
426    
427                    scProductEntryPersistence.setSCLicenses(productEntryId, licenseIds);
428    
429                    // Product screenshots
430    
431                    if (thumbnails.isEmpty()) {
432                            scProductScreenshotLocalService.deleteProductScreenshots(
433                                    productEntryId);
434                    }
435                    else {
436                            saveProductScreenshots(productEntry, thumbnails, fullImages);
437                    }
438    
439                    return productEntry;
440            }
441    
442            protected String getTags(String tags) {
443                    tags = StringUtil.toLowerCase(tags.trim());
444    
445                    return StringUtil.merge(StringUtil.split(tags), ", ");
446            }
447    
448            protected boolean isVersionSupported(
449                    String version, List<SCFrameworkVersion> frameworkVersions) {
450    
451                    Version currentVersion = Version.getInstance(version);
452    
453                    for (SCFrameworkVersion frameworkVersion : frameworkVersions) {
454                            Version supportedVersion = Version.getInstance(
455                                    frameworkVersion.getName());
456    
457                            if (supportedVersion.includes(currentVersion)) {
458                                    return true;
459                            }
460                    }
461    
462                    return false;
463            }
464    
465            protected void populatePluginPackageElement(
466                    Element el, SCProductEntry productEntry,
467                    SCProductVersion productVersion, String baseImageURL) {
468    
469                    DocUtil.add(el, "name", productEntry.getName());
470    
471                    String moduleId = ModuleId.toString(
472                            productEntry.getRepoGroupId(), productEntry.getRepoArtifactId(),
473                            productVersion.getVersion(), "war");
474    
475                    DocUtil.add(el, "module-id", moduleId);
476    
477                    DocUtil.add(
478                            el, "modified-date",
479                            Time.getRFC822(productVersion.getModifiedDate()));
480    
481                    Element typesEl = el.addElement("types");
482    
483                    DocUtil.add(typesEl, "type", productEntry.getType());
484    
485                    Element tagsEl = el.addElement("tags");
486    
487                    String[] tags = StringUtil.split(productEntry.getTags());
488    
489                    for (int i = 0; i < tags.length; i++) {
490                            DocUtil.add(tagsEl, "tag", tags[i]);
491                    }
492    
493                    DocUtil.add(
494                            el, "short-description", productEntry.getShortDescription());
495    
496                    if (Validator.isNotNull(productEntry.getLongDescription())) {
497                            DocUtil.add(
498                                    el, "long-description", productEntry.getLongDescription());
499                    }
500    
501                    if (Validator.isNotNull(productVersion.getChangeLog())) {
502                            DocUtil.add(el, "change-log", productVersion.getChangeLog());
503                    }
504    
505                    if (Validator.isNotNull(productVersion.getDirectDownloadURL())) {
506                            DocUtil.add(
507                                    el, "download-url", productVersion.getDirectDownloadURL());
508                    }
509    
510                    DocUtil.add(el, "author", productEntry.getAuthor());
511    
512                    Element screenshotsEl = el.addElement("screenshots");
513    
514                    for (SCProductScreenshot screenshot : productEntry.getScreenshots()) {
515                            long thumbnailId = screenshot.getThumbnailId();
516                            long fullImageId = screenshot.getFullImageId();
517    
518                            Element screenshotEl = screenshotsEl.addElement("screenshot");
519    
520                            DocUtil.add(
521                                    screenshotEl, "thumbnail-url",
522                                    baseImageURL + "?img_id=" + thumbnailId + "&t=" +
523                                            WebServerServletTokenUtil.getToken(thumbnailId));
524                            DocUtil.add(
525                                    screenshotEl, "large-image-url",
526                                    baseImageURL + "?img_id=" + fullImageId + "&t=" +
527                                            WebServerServletTokenUtil.getToken(fullImageId));
528                    }
529    
530                    Element licensesEl = el.addElement("licenses");
531    
532                    for (SCLicense license : productEntry.getLicenses()) {
533                            Element licenseEl = licensesEl.addElement("license");
534    
535                            licenseEl.addText(license.getName());
536                            licenseEl.addAttribute(
537                                    "osi-approved", String.valueOf(license.isOpenSource()));
538                    }
539    
540                    Element liferayVersionsEl = el.addElement("liferay-versions");
541    
542                    for (SCFrameworkVersion frameworkVersion :
543                                    productVersion.getFrameworkVersions()) {
544    
545                            DocUtil.add(
546                                    liferayVersionsEl, "liferay-version",
547                                    frameworkVersion.getName());
548                    }
549            }
550    
551            protected void populateSettingsElement(
552                    Element el, Properties repoSettings) {
553    
554                    if (repoSettings == null) {
555                            return;
556                    }
557    
558                    for (Map.Entry<Object, Object> entry : repoSettings.entrySet()) {
559                            String name = (String)entry.getKey();
560                            String value = (String)entry.getValue();
561    
562                            Element settingEl = el.addElement("setting");
563    
564                            settingEl.addAttribute("name", name);
565                            settingEl.addAttribute("value", value);
566                    }
567            }
568    
569            protected void saveProductScreenshots(
570                            SCProductEntry productEntry, List<byte[]> thumbnails,
571                            List<byte[]> fullImages)
572                    throws PortalException {
573    
574                    long productEntryId = productEntry.getProductEntryId();
575    
576                    List<SCProductScreenshot> productScreenshots =
577                            scProductScreenshotPersistence.findByProductEntryId(productEntryId);
578    
579                    if (thumbnails.size() < productScreenshots.size()) {
580                            for (int i = thumbnails.size(); i < productScreenshots.size();
581                                            i++) {
582    
583                                    SCProductScreenshot productScreenshot = productScreenshots.get(
584                                            i);
585    
586                                    scProductScreenshotLocalService.deleteProductScreenshot(
587                                            productScreenshot);
588                            }
589                    }
590    
591                    for (int i = 0; i < thumbnails.size(); i++) {
592                            int priority = i;
593    
594                            byte[] thumbnail = thumbnails.get(i);
595                            byte[] fullImage = fullImages.get(i);
596    
597                            SCProductScreenshot productScreenshot =
598                                    scProductScreenshotPersistence.fetchByP_P(
599                                            productEntryId, priority);
600    
601                            if (productScreenshot == null) {
602                                    long productScreenshotId = counterLocalService.increment();
603    
604                                    productScreenshot = scProductScreenshotPersistence.create(
605                                            productScreenshotId);
606    
607                                    productScreenshot.setCompanyId(productEntry.getCompanyId());
608                                    productScreenshot.setGroupId(productEntry.getGroupId());
609                                    productScreenshot.setProductEntryId(productEntryId);
610                                    productScreenshot.setThumbnailId(
611                                            counterLocalService.increment());
612                                    productScreenshot.setFullImageId(
613                                            counterLocalService.increment());
614                                    productScreenshot.setPriority(priority);
615    
616                                    scProductScreenshotPersistence.update(productScreenshot);
617                            }
618    
619                            imageLocalService.updateImage(
620                                    productScreenshot.getThumbnailId(), thumbnail);
621                            imageLocalService.updateImage(
622                                    productScreenshot.getFullImageId(), fullImage);
623                    }
624            }
625    
626            protected void validate(
627                            long productEntryId, String name, String type,
628                            String shortDescription, String pageURL, String author,
629                            String repoGroupId, String repoArtifactId, long[] licenseIds,
630                            List<byte[]> thumbnails, List<byte[]> fullImages)
631                    throws PortalException {
632    
633                    if (Validator.isNull(name)) {
634                            throw new ProductEntryNameException();
635                    }
636    
637                    if (Validator.isNull(type)) {
638                            throw new ProductEntryTypeException();
639                    }
640    
641                    if (Validator.isNull(shortDescription)) {
642                            throw new ProductEntryShortDescriptionException();
643                    }
644    
645                    if (Validator.isNull(pageURL)) {
646                            throw new ProductEntryPageURLException();
647                    }
648                    else if (!Validator.isUrl(pageURL)) {
649                            throw new ProductEntryPageURLException();
650                    }
651    
652                    if (Validator.isNull(author)) {
653                            throw new ProductEntryAuthorException();
654                    }
655    
656                    SCProductEntry productEntry = scProductEntryPersistence.fetchByRG_RA(
657                            repoGroupId, repoArtifactId);
658    
659                    if ((productEntry != null) &&
660                            (productEntry.getProductEntryId() != productEntryId)) {
661    
662                            throw new DuplicateProductEntryModuleIdException(
663                                    "{productEntryId=" + productEntryId + "}");
664                    }
665    
666                    if (licenseIds.length == 0) {
667                            throw new ProductEntryLicenseException();
668                    }
669    
670                    if (thumbnails.size() != fullImages.size()) {
671                            throw new ProductEntryScreenshotsException();
672                    }
673                    else {
674                            for (byte[] thumbnail : thumbnails) {
675                                    if (thumbnail == null) {
676                                            throw new ProductEntryScreenshotsException();
677                                    }
678                            }
679    
680                            for (byte[] fullImage : fullImages) {
681                                    if (fullImage == null) {
682                                            throw new ProductEntryScreenshotsException();
683                                    }
684                            }
685                    }
686            }
687    
688    }