1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.softwarecatalog.service.impl;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.search.BooleanClauseOccur;
28  import com.liferay.portal.kernel.search.BooleanQuery;
29  import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
30  import com.liferay.portal.kernel.search.Field;
31  import com.liferay.portal.kernel.search.Hits;
32  import com.liferay.portal.kernel.search.SearchEngineUtil;
33  import com.liferay.portal.kernel.search.SearchException;
34  import com.liferay.portal.kernel.servlet.ImageServletTokenUtil;
35  import com.liferay.portal.kernel.util.GetterUtil;
36  import com.liferay.portal.kernel.util.OrderByComparator;
37  import com.liferay.portal.kernel.util.StringPool;
38  import com.liferay.portal.kernel.util.StringUtil;
39  import com.liferay.portal.kernel.util.Time;
40  import com.liferay.portal.kernel.util.Validator;
41  import com.liferay.portal.model.ResourceConstants;
42  import com.liferay.portal.model.User;
43  import com.liferay.portal.plugin.ModuleId;
44  import com.liferay.portal.util.PortalUtil;
45  import com.liferay.portlet.softwarecatalog.DuplicateProductEntryModuleIdException;
46  import com.liferay.portlet.softwarecatalog.NoSuchProductEntryException;
47  import com.liferay.portlet.softwarecatalog.ProductEntryAuthorException;
48  import com.liferay.portlet.softwarecatalog.ProductEntryLicenseException;
49  import com.liferay.portlet.softwarecatalog.ProductEntryNameException;
50  import com.liferay.portlet.softwarecatalog.ProductEntryPageURLException;
51  import com.liferay.portlet.softwarecatalog.ProductEntryScreenshotsException;
52  import com.liferay.portlet.softwarecatalog.ProductEntryShortDescriptionException;
53  import com.liferay.portlet.softwarecatalog.ProductEntryTypeException;
54  import com.liferay.portlet.softwarecatalog.model.SCFrameworkVersion;
55  import com.liferay.portlet.softwarecatalog.model.SCLicense;
56  import com.liferay.portlet.softwarecatalog.model.SCProductEntry;
57  import com.liferay.portlet.softwarecatalog.model.SCProductScreenshot;
58  import com.liferay.portlet.softwarecatalog.model.SCProductVersion;
59  import com.liferay.portlet.softwarecatalog.service.base.SCProductEntryLocalServiceBaseImpl;
60  import com.liferay.portlet.softwarecatalog.util.Indexer;
61  import com.liferay.util.Version;
62  import com.liferay.util.xml.DocUtil;
63  
64  import java.net.MalformedURLException;
65  import java.net.URL;
66  
67  import java.util.Date;
68  import java.util.Iterator;
69  import java.util.List;
70  import java.util.Properties;
71  
72  import org.apache.commons.logging.Log;
73  import org.apache.commons.logging.LogFactory;
74  
75  import org.dom4j.Document;
76  import org.dom4j.DocumentHelper;
77  import org.dom4j.Element;
78  
79  /**
80   * <a href="SCProductEntryLocalServiceImpl.java.html"><b><i>View Source</i></b>
81   * </a>
82   *
83   * @author Jorge Ferrer
84   * @author Brian Wing Shun Chan
85   *
86   */
87  public class SCProductEntryLocalServiceImpl
88      extends SCProductEntryLocalServiceBaseImpl {
89  
90      public SCProductEntry addProductEntry(
91              long userId, long plid, String name, String type, String tags,
92              String shortDescription, String longDescription, String pageURL,
93              String author, String repoGroupId, String repoArtifactId,
94              long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages,
95              boolean addCommunityPermissions, boolean addGuestPermissions)
96          throws PortalException, SystemException {
97  
98          return addProductEntry(
99              userId, plid, name, type, tags, shortDescription, longDescription,
100             pageURL, author, repoGroupId, repoArtifactId, licenseIds,
101             thumbnails, fullImages, Boolean.valueOf(addCommunityPermissions),
102             Boolean.valueOf(addGuestPermissions), null, null);
103     }
104 
105     public SCProductEntry addProductEntry(
106             long userId, long plid, String name, String type, String tags,
107             String shortDescription, String longDescription, String pageURL,
108             String author, String repoGroupId, String repoArtifactId,
109             long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages,
110             String[] communityPermissions, String[] guestPermissions)
111         throws PortalException, SystemException {
112 
113         return addProductEntry(
114             userId, plid, name, type, tags, shortDescription, longDescription,
115             pageURL, author, repoGroupId, repoArtifactId, licenseIds,
116             thumbnails, fullImages, null, null, communityPermissions,
117             guestPermissions);
118     }
119 
120     public SCProductEntry addProductEntry(
121             long userId, long plid, String name, String type, String tags,
122             String shortDescription, String longDescription, String pageURL,
123             String author, String repoGroupId, String repoArtifactId,
124             long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages,
125             Boolean addCommunityPermissions, Boolean addGuestPermissions,
126             String[] communityPermissions, String[] guestPermissions)
127         throws PortalException, SystemException {
128 
129         // Product entry
130 
131         User user = userPersistence.findByPrimaryKey(userId);
132         long groupId = PortalUtil.getPortletGroupId(plid);
133         tags = getTags(tags);
134         repoGroupId = repoGroupId.trim().toLowerCase();
135         repoArtifactId = repoArtifactId.trim().toLowerCase();
136         Date now = new Date();
137 
138         validate(
139             0, name, type, shortDescription, pageURL, author, repoGroupId,
140             repoArtifactId, licenseIds, thumbnails, fullImages);
141 
142         long productEntryId = counterLocalService.increment();
143 
144         SCProductEntry productEntry = scProductEntryPersistence.create(
145             productEntryId);
146 
147         productEntry.setGroupId(groupId);
148         productEntry.setCompanyId(user.getCompanyId());
149         productEntry.setUserId(user.getUserId());
150         productEntry.setUserName(user.getFullName());
151         productEntry.setCreateDate(now);
152         productEntry.setModifiedDate(now);
153         productEntry.setName(name);
154         productEntry.setType(type);
155         productEntry.setTags(tags);
156         productEntry.setShortDescription(shortDescription);
157         productEntry.setLongDescription(longDescription);
158         productEntry.setPageURL(pageURL);
159         productEntry.setAuthor(author);
160         productEntry.setRepoGroupId(repoGroupId);
161         productEntry.setRepoArtifactId(repoArtifactId);
162 
163         scProductEntryPersistence.update(productEntry, false);
164 
165         // Resources
166 
167         if ((addCommunityPermissions != null) &&
168             (addGuestPermissions != null)) {
169 
170             addProductEntryResources(
171                 productEntry, addCommunityPermissions.booleanValue(),
172                 addGuestPermissions.booleanValue());
173         }
174         else {
175             addProductEntryResources(
176                 productEntry, communityPermissions, guestPermissions);
177         }
178 
179         // Licenses
180 
181         scProductEntryPersistence.setSCLicenses(productEntryId, licenseIds);
182 
183         // Product screenshots
184 
185         saveProductScreenshots(productEntry, thumbnails, fullImages);
186 
187         // Lucene
188 
189         try {
190             Indexer.addProductEntry(
191                 productEntry.getCompanyId(), groupId, userId,
192                 user.getFullName(), productEntryId, name, now, StringPool.BLANK,
193                 type, shortDescription, longDescription, pageURL, repoGroupId,
194                 repoArtifactId);
195         }
196         catch (SearchException se) {
197             _log.error("Indexing " + productEntryId, se);
198         }
199 
200         return productEntry;
201     }
202 
203     public void addProductEntryResources(
204             long productEntryId, boolean addCommunityPermissions,
205             boolean addGuestPermissions)
206         throws PortalException, SystemException {
207 
208         SCProductEntry productEntry =
209             scProductEntryPersistence.findByPrimaryKey(productEntryId);
210 
211         addProductEntryResources(
212             productEntry, addCommunityPermissions, addGuestPermissions);
213     }
214 
215     public void addProductEntryResources(
216             SCProductEntry productEntry, boolean addCommunityPermissions,
217             boolean addGuestPermissions)
218         throws PortalException, SystemException {
219 
220         resourceLocalService.addResources(
221             productEntry.getCompanyId(), productEntry.getGroupId(),
222             productEntry.getUserId(), SCProductEntry.class.getName(),
223             productEntry.getProductEntryId(), false, addCommunityPermissions,
224             addGuestPermissions);
225     }
226 
227     public void addProductEntryResources(
228             long productEntryId, String[] communityPermissions,
229             String[] guestPermissions)
230         throws PortalException, SystemException {
231 
232         SCProductEntry productEntry =
233             scProductEntryPersistence.findByPrimaryKey(productEntryId);
234 
235         addProductEntryResources(
236             productEntry, communityPermissions, guestPermissions);
237     }
238 
239     public void addProductEntryResources(
240             SCProductEntry productEntry, String[] communityPermissions,
241             String[] guestPermissions)
242         throws PortalException, SystemException {
243 
244         resourceLocalService.addModelResources(
245             productEntry.getCompanyId(), productEntry.getGroupId(),
246             productEntry.getUserId(), SCProductEntry.class.getName(),
247             productEntry.getProductEntryId(), communityPermissions,
248             guestPermissions);
249     }
250 
251     public void deleteProductEntries(long groupId)
252         throws PortalException, SystemException {
253 
254         List<SCProductEntry> productEntries =
255             scProductEntryPersistence.findByGroupId(groupId);
256 
257         for (SCProductEntry productEntry : productEntries) {
258             deleteProductEntry(productEntry);
259         }
260     }
261 
262     public void deleteProductEntry(long productEntryId)
263         throws PortalException, SystemException {
264 
265         SCProductEntry productEntry =
266             scProductEntryPersistence.findByPrimaryKey(productEntryId);
267 
268         deleteProductEntry(productEntry);
269     }
270 
271     public void deleteProductEntry(SCProductEntry productEntry)
272         throws PortalException, SystemException {
273 
274         // Lucene
275 
276         try {
277             Indexer.deleteProductEntry(
278                 productEntry.getCompanyId(), productEntry.getProductEntryId());
279         }
280         catch (SearchException se) {
281             _log.error(
282                 "Deleting index " + productEntry.getProductEntryId(), se);
283         }
284 
285         // Product screenshots
286 
287         scProductScreenshotLocalService.deleteProductScreenshots(
288             productEntry.getProductEntryId());
289 
290         // Product versions
291 
292         scProductVersionLocalService.deleteProductVersions(
293             productEntry.getProductEntryId());
294 
295         // Ratings
296 
297         ratingsStatsLocalService.deleteStats(
298             SCProductEntry.class.getName(), productEntry.getProductEntryId());
299 
300         // Message boards
301 
302         mbMessageLocalService.deleteDiscussionMessages(
303             SCProductEntry.class.getName(), productEntry.getProductEntryId());
304 
305         // Resources
306 
307         resourceLocalService.deleteResource(
308             productEntry.getCompanyId(), SCProductEntry.class.getName(),
309             ResourceConstants.SCOPE_INDIVIDUAL,
310             productEntry.getProductEntryId());
311 
312         // Product entry
313 
314         scProductEntryPersistence.remove(productEntry);
315     }
316 
317     public SCProductEntry getProductEntry(long productEntryId)
318         throws PortalException, SystemException {
319 
320         return scProductEntryPersistence.findByPrimaryKey(productEntryId);
321     }
322 
323     public List<SCProductEntry> getProductEntries(
324             long groupId, int start, int end)
325         throws SystemException {
326 
327         return scProductEntryPersistence.findByGroupId(groupId, start, end);
328     }
329 
330     public List<SCProductEntry> getProductEntries(
331             long groupId, int start, int end, OrderByComparator obc)
332         throws SystemException {
333 
334         return scProductEntryPersistence.findByGroupId(
335             groupId, start, end, obc);
336     }
337 
338     public List<SCProductEntry> getProductEntries(
339             long groupId, long userId, int start, int end)
340         throws SystemException {
341 
342         return scProductEntryPersistence.findByG_U(groupId, userId, start, end);
343     }
344 
345     public List<SCProductEntry> getProductEntries(
346             long groupId, long userId, int start, int end,
347             OrderByComparator obc)
348         throws SystemException {
349 
350         return scProductEntryPersistence.findByG_U(
351             groupId, userId, start, end, obc);
352     }
353 
354     public int getProductEntriesCount(long groupId)
355         throws SystemException {
356 
357         return scProductEntryPersistence.countByGroupId(groupId);
358     }
359 
360     public int getProductEntriesCount(long groupId, long userId)
361         throws SystemException {
362 
363         return scProductEntryPersistence.countByG_U(groupId, userId);
364     }
365 
366     public String getRepositoryXML(
367             long groupId, String baseImageURL, Date oldestDate,
368             int maxNumOfVersions, Properties repoSettings)
369         throws PortalException, SystemException {
370 
371         return getRepositoryXML(
372             groupId, null, baseImageURL, oldestDate, maxNumOfVersions,
373             repoSettings);
374     }
375 
376     public String getRepositoryXML(
377             long groupId, String version, String baseImageURL, Date oldestDate,
378             int maxNumOfVersions, Properties repoSettings)
379         throws PortalException, SystemException {
380 
381         Document doc = DocumentHelper.createDocument();
382 
383         doc.setXMLEncoding(StringPool.UTF8);
384 
385         Element root = doc.addElement("plugin-repository");
386 
387         Element settingsEl = root.addElement("settings");
388 
389         populateSettingsElement(settingsEl, repoSettings);
390 
391         List<SCProductEntry> productEntries =
392             scProductEntryPersistence.findByGroupId(groupId);
393 
394         for (SCProductEntry productEntry : productEntries) {
395             if (Validator.isNull(productEntry.getRepoGroupId()) ||
396                 Validator.isNull(productEntry.getRepoArtifactId())) {
397 
398                 continue;
399             }
400 
401             List<SCProductVersion> productVersions =
402                 scProductVersionPersistence.findByProductEntryId(
403                     productEntry.getProductEntryId());
404 
405             for (int i = 0; i < productVersions.size(); i++) {
406                 SCProductVersion productVersion = productVersions.get(i);
407 
408                 if ((maxNumOfVersions > 0) && (maxNumOfVersions < (i + 1))) {
409                     break;
410                 }
411 
412                 if (!productVersion.isRepoStoreArtifact()) {
413                     continue;
414                 }
415 
416                 if ((oldestDate != null) &&
417                     (oldestDate.after(productVersion.getModifiedDate()))) {
418 
419                     continue;
420                 }
421 
422                 if (Validator.isNotNull(version) &&
423                     !isVersionSupported(
424                         version, productVersion.getFrameworkVersions())) {
425 
426                     continue;
427                 }
428 
429                 Element el = root.addElement("plugin-package");
430 
431                 populatePluginPackageElement(
432                     el, productEntry, productVersion, baseImageURL);
433             }
434         }
435 
436         return doc.asXML();
437     }
438 
439     public void reIndex(String[] ids) throws SystemException {
440         if (SearchEngineUtil.isIndexReadOnly()) {
441             return;
442         }
443 
444         long companyId = GetterUtil.getLong(ids[0]);
445 
446         try {
447             List<SCProductEntry> productEntries =
448                 scProductEntryPersistence.findByCompanyId(companyId);
449 
450             for (SCProductEntry productEntry : productEntries) {
451                 long productEntryId = productEntry.getProductEntryId();
452 
453                 String version = StringPool.BLANK;
454 
455                 SCProductVersion latestProductVersion =
456                     productEntry.getLatestVersion();
457 
458                 if (latestProductVersion != null) {
459                     version = latestProductVersion.getVersion();
460                 }
461 
462                 try {
463                     com.liferay.portal.kernel.search.Document doc =
464                         Indexer.getProductEntryDocument(
465                             companyId, productEntry.getGroupId(),
466                             productEntry.getUserId(),
467                             productEntry.getUserName(),
468                             productEntryId, productEntry.getName(),
469                             productEntry.getModifiedDate(), version,
470                             productEntry.getType(),
471                             productEntry.getShortDescription(),
472                             productEntry.getLongDescription(),
473                             productEntry.getPageURL(),
474                             productEntry.getRepoGroupId(),
475                             productEntry.getRepoArtifactId());
476 
477                     SearchEngineUtil.addDocument(companyId, doc);
478                 }
479                 catch (Exception e1) {
480                     _log.error("Reindexing " + productEntryId, e1);
481                 }
482             }
483         }
484         catch (SystemException se) {
485             throw se;
486         }
487         catch (Exception e2) {
488             throw new SystemException(e2);
489         }
490     }
491 
492     public Hits search(
493             long companyId, long groupId, String keywords, String type,
494             int start, int end)
495         throws SystemException {
496 
497         try {
498             BooleanQuery contextQuery = BooleanQueryFactoryUtil.create();
499 
500             contextQuery.addRequiredTerm(Field.PORTLET_ID, Indexer.PORTLET_ID);
501             contextQuery.addRequiredTerm(Field.GROUP_ID, groupId);
502 
503             BooleanQuery fullQuery = BooleanQueryFactoryUtil.create();
504 
505             fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
506 
507             if (Validator.isNotNull(keywords)) {
508                 BooleanQuery searchQuery = BooleanQueryFactoryUtil.create();
509 
510                 searchQuery.addTerm(Field.NAME, keywords);
511                 searchQuery.addTerm(Field.CONTENT, keywords);
512 
513                 fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
514             }
515 
516             if (Validator.isNotNull(type)) {
517                 BooleanQuery searchQuery = BooleanQueryFactoryUtil.create();
518 
519                 searchQuery.addRequiredTerm("type", type);
520 
521                 fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
522             }
523 
524             return SearchEngineUtil.search(companyId, fullQuery, start, end);
525         }
526         catch (Exception e) {
527             throw new SystemException(e);
528         }
529     }
530 
531     public SCProductEntry updateProductEntry(
532             long productEntryId, String name, String type, String tags,
533             String shortDescription, String longDescription, String pageURL,
534             String author, String repoGroupId, String repoArtifactId,
535             long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages)
536         throws PortalException, SystemException {
537 
538         // Product entry
539 
540         tags = getTags(tags);
541         repoGroupId = repoGroupId.trim().toLowerCase();
542         repoArtifactId = repoArtifactId.trim().toLowerCase();
543         Date now = new Date();
544 
545         validate(
546             productEntryId, name, type, shortDescription, pageURL, author,
547             repoGroupId, repoArtifactId, licenseIds, thumbnails, fullImages);
548 
549         SCProductEntry productEntry =
550             scProductEntryPersistence.findByPrimaryKey(productEntryId);
551 
552         productEntry.setModifiedDate(now);
553         productEntry.setName(name);
554         productEntry.setType(type);
555         productEntry.setTags(tags);
556         productEntry.setShortDescription(shortDescription);
557         productEntry.setLongDescription(longDescription);
558         productEntry.setPageURL(pageURL);
559         productEntry.setAuthor(author);
560         productEntry.setRepoGroupId(repoGroupId);
561         productEntry.setRepoArtifactId(repoArtifactId);
562 
563         scProductEntryPersistence.update(productEntry, false);
564 
565         // Licenses
566 
567         scProductEntryPersistence.setSCLicenses(productEntryId, licenseIds);
568 
569         // Product screenshots
570 
571         if (thumbnails.size() == 0) {
572             scProductScreenshotLocalService.deleteProductScreenshots(
573                 productEntryId);
574         }
575         else {
576             saveProductScreenshots(productEntry, thumbnails, fullImages);
577         }
578 
579         // Latest product version
580 
581         String version = StringPool.BLANK;
582 
583         List<SCProductVersion> productVersions =
584             scProductVersionPersistence.findByProductEntryId(
585                 productEntryId, 0, 1);
586 
587         if (productVersions.size() > 0) {
588             SCProductVersion productVersion = productVersions.get(0);
589 
590             productVersion.setModifiedDate(now);
591 
592             scProductVersionPersistence.update(productVersion, false);
593 
594             version = productVersion.getVersion();
595         }
596 
597         // Lucene
598 
599         try {
600             Indexer.updateProductEntry(
601                 productEntry.getCompanyId(), productEntry.getGroupId(),
602                 productEntry.getUserId(), productEntry.getUserName(),
603                 productEntryId, name, now, version, type, shortDescription,
604                 longDescription, pageURL, repoGroupId, repoArtifactId);
605         }
606         catch (SearchException se) {
607             _log.error("Indexing " + productEntryId, se);
608         }
609 
610         return productEntry;
611     }
612 
613     protected String getTags(String tags) {
614         tags = tags.trim().toLowerCase();
615 
616         return StringUtil.merge(StringUtil.split(tags), ", ");
617     }
618 
619     protected boolean isVersionSupported(
620         String version, List<SCFrameworkVersion> frameworkVersions) {
621 
622         Version currentVersion = Version.getInstance(version);
623 
624         for (SCFrameworkVersion frameworkVersion : frameworkVersions) {
625             Version supportedVersion = Version.getInstance(
626                 frameworkVersion.getName());
627 
628             if (supportedVersion.includes(currentVersion)) {
629                 return true;
630             }
631         }
632 
633         return false;
634     }
635 
636     protected void populatePluginPackageElement(
637             Element el, SCProductEntry productEntry,
638             SCProductVersion productVersion, String baseImageURL)
639         throws PortalException, SystemException {
640 
641         DocUtil.add(el, "name", productEntry.getName());
642 
643         String moduleId = ModuleId.toString(
644             productEntry.getRepoGroupId(), productEntry.getRepoArtifactId(),
645             productVersion.getVersion(), "war");
646 
647         DocUtil.add(el, "module-id", moduleId);
648 
649         DocUtil.add(
650             el, "modified-date",
651             Time.getRFC822(productVersion.getModifiedDate()));
652 
653         Element typesEl = el.addElement("types");
654 
655         DocUtil.add(typesEl, "type", productEntry.getType());
656 
657         Element tagsEl = el.addElement("tags");
658 
659         String[] tags = StringUtil.split(productEntry.getTags());
660 
661         for (int i = 0; i < tags.length; i++) {
662             DocUtil.add(tagsEl, "tag", tags[i]);
663         }
664 
665         DocUtil.add(
666             el, "short-description", productEntry.getShortDescription());
667 
668         if (Validator.isNotNull(productEntry.getLongDescription())) {
669             DocUtil.add(
670                 el, "long-description", productEntry.getLongDescription());
671         }
672 
673         if (Validator.isNotNull(productVersion.getChangeLog())) {
674             DocUtil.add(el, "change-log", productVersion.getChangeLog());
675         }
676 
677         if (Validator.isNotNull(productVersion.getDirectDownloadURL())) {
678             DocUtil.add(
679                 el, "download-url", productVersion.getDirectDownloadURL());
680         }
681 
682         DocUtil.add(el, "author", productEntry.getAuthor());
683 
684         Element screenshotsEl = el.addElement("screenshots");
685 
686         for (SCProductScreenshot screenshot : productEntry.getScreenshots()) {
687             long thumbnailId = screenshot.getThumbnailId();
688             long fullImageId = screenshot.getFullImageId();
689 
690             Element screenshotEl = screenshotsEl.addElement("screenshot");
691 
692             DocUtil.add(
693                 screenshotEl, "thumbnail-url",
694                 baseImageURL + "?img_id=" + thumbnailId + "&t=" +
695                     ImageServletTokenUtil.getToken(thumbnailId));
696             DocUtil.add(
697                 screenshotEl, "large-image-url",
698                 baseImageURL + "?img_id=" + fullImageId + "&t=" +
699                     ImageServletTokenUtil.getToken(fullImageId));
700         }
701 
702         Element licensesEl = el.addElement("licenses");
703 
704         for (SCLicense license : productEntry.getLicenses()) {
705             Element licenseEl = licensesEl.addElement("license");
706 
707             licenseEl.addText(license.getName());
708             licenseEl.addAttribute(
709                 "osi-approved", String.valueOf(license.isOpenSource()));
710         }
711 
712         Element liferayVersionsEl = el.addElement("liferay-versions");
713 
714         for (SCFrameworkVersion frameworkVersion :
715                 productVersion.getFrameworkVersions()) {
716 
717             DocUtil.add(
718                 liferayVersionsEl, "liferay-version",
719                 frameworkVersion.getName());
720         }
721     }
722 
723     protected void populateSettingsElement(
724         Element el, Properties repoSettings) {
725 
726         if (repoSettings == null) {
727             return;
728         }
729 
730         Iterator<Object> itr = repoSettings.keySet().iterator();
731 
732         while (itr.hasNext()) {
733             String key = (String)itr.next();
734 
735             Element settingEl = el.addElement("setting");
736 
737             settingEl.addAttribute("name", key);
738             settingEl.addAttribute("value", repoSettings.getProperty(key));
739         }
740     }
741 
742     protected void saveProductScreenshots(
743             SCProductEntry productEntry, List<byte[]> thumbnails,
744             List<byte[]> fullImages)
745         throws PortalException, SystemException {
746 
747         long productEntryId = productEntry.getProductEntryId();
748 
749         List<SCProductScreenshot> productScreenshots =
750             scProductScreenshotPersistence.findByProductEntryId(productEntryId);
751 
752         if (thumbnails.size() < productScreenshots.size()) {
753             for (int i = thumbnails.size(); i < productScreenshots.size();
754                     i++) {
755 
756                 SCProductScreenshot productScreenshot =
757                     productScreenshots.get(i);
758 
759                 scProductScreenshotLocalService.deleteProductScreenshot(
760                     productScreenshot);
761             }
762         }
763 
764         for (int i = 0; i < thumbnails.size(); i++) {
765             int priority = i;
766 
767             byte[] thumbnail = thumbnails.get(i);
768             byte[] fullImage = fullImages.get(i);
769 
770             SCProductScreenshot productScreenshot =
771                 scProductScreenshotPersistence.fetchByP_P(
772                     productEntryId, priority);
773 
774             if (productScreenshot == null) {
775                 long productScreenshotId = counterLocalService.increment();
776 
777                 productScreenshot = scProductScreenshotPersistence.create(
778                     productScreenshotId);
779 
780                 productScreenshot.setCompanyId(productEntry.getCompanyId());
781                 productScreenshot.setGroupId(productEntry.getGroupId());
782                 productScreenshot.setProductEntryId(productEntryId);
783                 productScreenshot.setThumbnailId(
784                     counterLocalService.increment());
785                 productScreenshot.setFullImageId(
786                     counterLocalService.increment());
787                 productScreenshot.setPriority(priority);
788 
789                 scProductScreenshotPersistence.update(productScreenshot, false);
790             }
791 
792             imageLocalService.updateImage(
793                 productScreenshot.getThumbnailId(), thumbnail);
794             imageLocalService.updateImage(
795                 productScreenshot.getFullImageId(), fullImage);
796         }
797     }
798 
799     protected void validate(
800             long productEntryId, String name, String type,
801             String shortDescription, String pageURL, String author,
802             String repoGroupId, String repoArtifactId, long[] licenseIds,
803             List<byte[]> thumbnails, List<byte[]> fullImages)
804         throws PortalException, SystemException {
805 
806         if (Validator.isNull(name)) {
807             throw new ProductEntryNameException();
808         }
809 
810         if (Validator.isNull(type)) {
811             throw new ProductEntryTypeException();
812         }
813 
814         if (Validator.isNull(shortDescription)) {
815             throw new ProductEntryShortDescriptionException();
816         }
817 
818         if (Validator.isNull(pageURL)) {
819             throw new ProductEntryPageURLException();
820         }
821         else {
822             try {
823                 new URL(pageURL);
824             }
825             catch (MalformedURLException murle) {
826                 throw new ProductEntryPageURLException();
827             }
828         }
829 
830         if (Validator.isNull(author)) {
831             throw new ProductEntryAuthorException();
832         }
833 
834         try {
835             SCProductEntry productEntry = scProductEntryPersistence.findByRG_RA(
836                 repoGroupId, repoArtifactId);
837 
838             if (productEntry.getProductEntryId() != productEntryId) {
839                 throw new DuplicateProductEntryModuleIdException();
840             }
841         }
842         catch (NoSuchProductEntryException nspee) {
843         }
844 
845         if (licenseIds.length == 0) {
846             throw new ProductEntryLicenseException();
847         }
848 
849         if (thumbnails.size() != fullImages.size()) {
850             throw new ProductEntryScreenshotsException();
851         }
852         else {
853             Iterator<byte[]> itr = thumbnails.iterator();
854 
855             while (itr.hasNext()) {
856                 if (itr.next() == null) {
857                     throw new ProductEntryScreenshotsException();
858                 }
859             }
860 
861             itr = fullImages.iterator();
862 
863             while (itr.hasNext()) {
864                 if (itr.next() == null) {
865                     throw new ProductEntryScreenshotsException();
866                 }
867             }
868         }
869     }
870 
871     private static Log _log =
872         LogFactory.getLog(SCProductEntryLocalServiceImpl.class);
873 
874 }