001    /**
002     * Copyright (c) 2000-2013 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.assetpublisher.util;
016    
017    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
018    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
019    import com.liferay.portal.kernel.dao.orm.Property;
020    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
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.servlet.SessionMessages;
027    import com.liferay.portal.kernel.util.Accessor;
028    import com.liferay.portal.kernel.util.ArrayUtil;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.ListUtil;
031    import com.liferay.portal.kernel.util.LocaleUtil;
032    import com.liferay.portal.kernel.util.LocalizationUtil;
033    import com.liferay.portal.kernel.util.ParamUtil;
034    import com.liferay.portal.kernel.util.PrefsPropsUtil;
035    import com.liferay.portal.kernel.util.PrimitiveLongList;
036    import com.liferay.portal.kernel.util.PropsKeys;
037    import com.liferay.portal.kernel.util.PropsUtil;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.kernel.xml.Document;
042    import com.liferay.portal.kernel.xml.Element;
043    import com.liferay.portal.kernel.xml.SAXReaderUtil;
044    import com.liferay.portal.model.Group;
045    import com.liferay.portal.model.GroupConstants;
046    import com.liferay.portal.model.Layout;
047    import com.liferay.portal.model.PortletConstants;
048    import com.liferay.portal.model.User;
049    import com.liferay.portal.security.auth.PrincipalThreadLocal;
050    import com.liferay.portal.security.permission.ActionKeys;
051    import com.liferay.portal.security.permission.PermissionChecker;
052    import com.liferay.portal.service.GroupLocalServiceUtil;
053    import com.liferay.portal.service.LayoutLocalServiceUtil;
054    import com.liferay.portal.service.PortletPreferencesLocalServiceUtil;
055    import com.liferay.portal.service.SubscriptionLocalServiceUtil;
056    import com.liferay.portal.service.permission.GroupPermissionUtil;
057    import com.liferay.portal.service.permission.PortletPermissionUtil;
058    import com.liferay.portal.service.persistence.PortletPreferencesActionableDynamicQuery;
059    import com.liferay.portal.theme.ThemeDisplay;
060    import com.liferay.portal.util.PortalUtil;
061    import com.liferay.portal.util.PortletKeys;
062    import com.liferay.portal.util.PropsValues;
063    import com.liferay.portal.util.SubscriptionSender;
064    import com.liferay.portal.util.WebKeys;
065    import com.liferay.portlet.PortletPreferencesFactoryUtil;
066    import com.liferay.portlet.asset.AssetRendererFactoryRegistryUtil;
067    import com.liferay.portlet.asset.model.AssetCategory;
068    import com.liferay.portlet.asset.model.AssetEntry;
069    import com.liferay.portlet.asset.model.AssetRenderer;
070    import com.liferay.portlet.asset.model.AssetRendererFactory;
071    import com.liferay.portlet.asset.model.AssetTag;
072    import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
073    import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
074    import com.liferay.portlet.asset.service.AssetEntryServiceUtil;
075    import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
076    import com.liferay.portlet.asset.service.persistence.AssetEntryQuery;
077    import com.liferay.portlet.expando.model.ExpandoBridge;
078    import com.liferay.portlet.sites.util.SitesUtil;
079    import com.liferay.util.ContentUtil;
080    
081    import java.io.IOException;
082    import java.io.Serializable;
083    
084    import java.util.ArrayList;
085    import java.util.HashMap;
086    import java.util.Iterator;
087    import java.util.List;
088    import java.util.Locale;
089    import java.util.Map;
090    
091    import javax.portlet.PortletException;
092    import javax.portlet.PortletPreferences;
093    import javax.portlet.PortletRequest;
094    
095    import javax.servlet.http.HttpServletRequest;
096    import javax.servlet.http.HttpSession;
097    
098    /**
099     * @author Raymond Aug??
100     * @author Julio Camarero
101     */
102    public class AssetPublisherImpl implements AssetPublisher {
103    
104            @Override
105            public void addAndStoreSelection(
106                            PortletRequest portletRequest, String className, long classPK,
107                            int assetEntryOrder)
108                    throws Exception {
109    
110                    String referringPortletResource = ParamUtil.getString(
111                            portletRequest, "referringPortletResource");
112    
113                    if (Validator.isNull(referringPortletResource)) {
114                            return;
115                    }
116    
117                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
118                            WebKeys.THEME_DISPLAY);
119    
120                    Layout layout = LayoutLocalServiceUtil.getLayout(
121                            themeDisplay.getRefererPlid());
122    
123                    PortletPreferences portletPreferences =
124                            PortletPreferencesFactoryUtil.getPortletSetup(
125                                    themeDisplay.getScopeGroupId(), layout,
126                                    referringPortletResource, null);
127    
128                    String selectionStyle = portletPreferences.getValue(
129                            "selectionStyle", "dynamic");
130    
131                    if (selectionStyle.equals("dynamic")) {
132                            return;
133                    }
134    
135                    AssetEntry assetEntry = AssetEntryLocalServiceUtil.getEntry(
136                            className, classPK);
137    
138                    addSelection(
139                            themeDisplay, portletPreferences, referringPortletResource,
140                            assetEntry.getEntryId(), assetEntryOrder, className);
141    
142                    portletPreferences.store();
143            }
144    
145            @Override
146            public void addRecentFolderId(
147                    PortletRequest portletRequest, String className, long classPK) {
148    
149                    _getRecentFolderIds(portletRequest).put(className, classPK);
150            }
151    
152            @Override
153            public void addSelection(
154                            PortletRequest portletRequest,
155                            PortletPreferences portletPreferences, String portletId)
156                    throws Exception {
157    
158                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
159                            WebKeys.THEME_DISPLAY);
160    
161                    long assetEntryId = ParamUtil.getLong(portletRequest, "assetEntryId");
162                    int assetEntryOrder = ParamUtil.getInteger(
163                            portletRequest, "assetEntryOrder");
164                    String assetEntryType = ParamUtil.getString(
165                            portletRequest, "assetEntryType");
166    
167                    addSelection(
168                            themeDisplay, portletPreferences, portletId, assetEntryId,
169                            assetEntryOrder, assetEntryType);
170            }
171    
172            @Override
173            public void addSelection(
174                            ThemeDisplay themeDisplay, PortletPreferences portletPreferences,
175                            String portletId, long assetEntryId, int assetEntryOrder,
176                            String assetEntryType)
177                    throws Exception {
178    
179                    AssetEntry assetEntry = AssetEntryLocalServiceUtil.getEntry(
180                            assetEntryId);
181    
182                    String[] assetEntryXmls = portletPreferences.getValues(
183                            "assetEntryXml", new String[0]);
184    
185                    String assetEntryXml = _getAssetEntryXml(
186                            assetEntryType, assetEntry.getClassUuid());
187    
188                    if (!ArrayUtil.contains(assetEntryXmls, assetEntryXml)) {
189                            if (assetEntryOrder > -1) {
190                                    assetEntryXmls[assetEntryOrder] = assetEntryXml;
191                            }
192                            else {
193                                    assetEntryXmls = ArrayUtil.append(
194                                            assetEntryXmls, assetEntryXml);
195                            }
196    
197                            portletPreferences.setValues("assetEntryXml", assetEntryXmls);
198                    }
199    
200                    long plid = themeDisplay.getRefererPlid();
201    
202                    if (plid == 0) {
203                            plid = themeDisplay.getPlid();
204                    }
205    
206                    List<AssetEntry> assetEntries = new ArrayList<AssetEntry>();
207    
208                    assetEntries.add(assetEntry);
209    
210                    notifySubscribers(portletPreferences, plid, portletId, assetEntries);
211            }
212    
213            @Override
214            public void addUserAttributes(
215                            User user, String[] customUserAttributeNames,
216                            AssetEntryQuery assetEntryQuery)
217                    throws Exception {
218    
219                    if ((user == null) || (customUserAttributeNames.length == 0)) {
220                            return;
221                    }
222    
223                    Group companyGroup = GroupLocalServiceUtil.getCompanyGroup(
224                            user.getCompanyId());
225    
226                    long[] allCategoryIds = assetEntryQuery.getAllCategoryIds();
227    
228                    PrimitiveLongList allCategoryIdsList = new PrimitiveLongList(
229                            allCategoryIds.length + customUserAttributeNames.length);
230    
231                    allCategoryIdsList.addAll(allCategoryIds);
232    
233                    for (String customUserAttributeName : customUserAttributeNames) {
234                            ExpandoBridge userCustomAttributes = user.getExpandoBridge();
235    
236                            Serializable userCustomFieldValue =
237                                    userCustomAttributes.getAttribute(customUserAttributeName);
238    
239                            if (userCustomFieldValue == null) {
240                                    continue;
241                            }
242    
243                            String userCustomFieldValueString = userCustomFieldValue.toString();
244    
245                            List<AssetCategory> assetCategories =
246                                    AssetCategoryLocalServiceUtil.search(
247                                            companyGroup.getGroupId(), userCustomFieldValueString,
248                                            new String[0], QueryUtil.ALL_POS, QueryUtil.ALL_POS);
249    
250                            for (AssetCategory assetCategory : assetCategories) {
251                                    allCategoryIdsList.add(assetCategory.getCategoryId());
252                            }
253                    }
254    
255                    assetEntryQuery.setAllCategoryIds(allCategoryIdsList.getArray());
256            }
257    
258            @Override
259            public void checkAssetEntries() throws Exception {
260                    ActionableDynamicQuery actionableDynamicQuery =
261                            new PortletPreferencesActionableDynamicQuery() {
262    
263                            @Override
264                            protected void addCriteria(DynamicQuery dynamicQuery) {
265                                    Property property = PropertyFactoryUtil.forName("portletId");
266    
267                                    String portletId =
268                                            PortletKeys.ASSET_PUBLISHER +
269                                                    PortletConstants.INSTANCE_SEPARATOR +
270                                                            StringPool.PERCENT;
271    
272                                    dynamicQuery.add(property.like(portletId));
273                            }
274    
275                            @Override
276                            protected void performAction(Object object)
277                                    throws PortalException, SystemException {
278    
279                                    com.liferay.portal.model.PortletPreferences portletPreferences =
280                                            (com.liferay.portal.model.PortletPreferences)object;
281    
282                                    _checkAssetEntries(portletPreferences);
283                            }
284    
285                    };
286    
287                    actionableDynamicQuery.performActions();
288            }
289    
290            @Override
291            public List<AssetEntry> getAssetEntries(
292                            PortletPreferences preferences, Layout layout, long scopeGroupId,
293                            int max, boolean checkPermission)
294                    throws PortalException, SystemException {
295    
296                    AssetEntryQuery assetEntryQuery = getAssetEntryQuery(
297                            preferences, new long[] {scopeGroupId});
298    
299                    boolean anyAssetType = GetterUtil.getBoolean(
300                            preferences.getValue("anyAssetType", null), true);
301    
302                    if (!anyAssetType) {
303                            long[] availableClassNameIds =
304                                    AssetRendererFactoryRegistryUtil.getClassNameIds(
305                                            layout.getCompanyId());
306    
307                            long[] classNameIds = getClassNameIds(
308                                    preferences, availableClassNameIds);
309    
310                            assetEntryQuery.setClassNameIds(classNameIds);
311                    }
312    
313                    long[] classTypeIds = GetterUtil.getLongValues(
314                            preferences.getValues("classTypeIds", null));
315    
316                    assetEntryQuery.setClassTypeIds(classTypeIds);
317    
318                    boolean enablePermissions = GetterUtil.getBoolean(
319                            preferences.getValue("enablePermissions", null));
320    
321                    assetEntryQuery.setEnablePermissions(enablePermissions);
322    
323                    assetEntryQuery.setEnd(max);
324    
325                    boolean excludeZeroViewCount = GetterUtil.getBoolean(
326                            preferences.getValue("excludeZeroViewCount", null));
327    
328                    assetEntryQuery.setExcludeZeroViewCount(excludeZeroViewCount);
329    
330                    long[] groupIds = getGroupIds(preferences, scopeGroupId, layout);
331    
332                    assetEntryQuery.setGroupIds(groupIds);
333    
334                    boolean showOnlyLayoutAssets = GetterUtil.getBoolean(
335                            preferences.getValue("showOnlyLayoutAssets", null));
336    
337                    if (showOnlyLayoutAssets) {
338                            assetEntryQuery.setLayout(layout);
339                    }
340    
341                    String orderByColumn1 = GetterUtil.getString(
342                            preferences.getValue("orderByColumn1", "modifiedDate"));
343    
344                    assetEntryQuery.setOrderByCol1(orderByColumn1);
345    
346                    String orderByColumn2 = GetterUtil.getString(
347                            preferences.getValue("orderByColumn2", "title"));
348    
349                    assetEntryQuery.setOrderByCol2(orderByColumn2);
350    
351                    String orderByType1 = GetterUtil.getString(
352                            preferences.getValue("orderByType1", "DESC"));
353    
354                    assetEntryQuery.setOrderByType1(orderByType1);
355    
356                    String orderByType2 = GetterUtil.getString(
357                            preferences.getValue("orderByType2", "ASC"));
358    
359                    assetEntryQuery.setOrderByType2(orderByType2);
360    
361                    assetEntryQuery.setStart(0);
362    
363                    if (checkPermission) {
364                            return AssetEntryServiceUtil.getEntries(assetEntryQuery);
365                    }
366                    else {
367                            return AssetEntryLocalServiceUtil.getEntries(assetEntryQuery);
368                    }
369            }
370    
371            @Override
372            public List<AssetEntry> getAssetEntries(
373                            PortletRequest portletRequest,
374                            PortletPreferences portletPreferences,
375                            PermissionChecker permissionChecker, long[] groupIds,
376                            long[] allCategoryIds, String[] assetEntryXmls,
377                            String[] allTagNames, boolean deleteMissingAssetEntries,
378                            boolean checkPermission)
379                    throws Exception {
380    
381                    List<AssetEntry> assetEntries = getAssetEntries(
382                            portletRequest, portletPreferences, permissionChecker, groupIds,
383                            assetEntryXmls, deleteMissingAssetEntries, checkPermission);
384    
385                    if (assetEntries.isEmpty() ||
386                            (ArrayUtil.isEmpty(allCategoryIds) &&
387                             ArrayUtil.isEmpty(allTagNames))) {
388    
389                            return assetEntries;
390                    }
391    
392                    if (!ArrayUtil.isEmpty(allCategoryIds)) {
393                            assetEntries = _filterAssetCategoriesAssetEntries(
394                                    assetEntries, allCategoryIds);
395                    }
396    
397                    if (!ArrayUtil.isEmpty(allTagNames)) {
398                            assetEntries = _filterAssetTagNamesAssetEntries(
399                                    assetEntries, allTagNames);
400                    }
401    
402                    return assetEntries;
403            }
404    
405            @Override
406            public List<AssetEntry> getAssetEntries(
407                            PortletRequest portletRequest,
408                            PortletPreferences portletPreferences,
409                            PermissionChecker permissionChecker, long[] groupIds,
410                            String[] assetEntryXmls, boolean deleteMissingAssetEntries,
411                            boolean checkPermission)
412                    throws Exception {
413    
414                    List<AssetEntry> assetEntries = new ArrayList<AssetEntry>();
415    
416                    List<String> missingAssetEntryUuids = new ArrayList<String>();
417    
418                    for (String assetEntryXml : assetEntryXmls) {
419                            Document document = SAXReaderUtil.read(assetEntryXml);
420    
421                            Element rootElement = document.getRootElement();
422    
423                            String assetEntryUuid = rootElement.elementText("asset-entry-uuid");
424    
425                            AssetEntry assetEntry = null;
426    
427                            for (long groupId : groupIds) {
428                                    assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
429                                            groupId, assetEntryUuid);
430    
431                                    if (assetEntry != null) {
432                                            break;
433                                    }
434                            }
435    
436                            if (assetEntry == null) {
437                                    if (deleteMissingAssetEntries) {
438                                            missingAssetEntryUuids.add(assetEntryUuid);
439                                    }
440    
441                                    continue;
442                            }
443    
444                            if (!assetEntry.isVisible()) {
445                                    continue;
446                            }
447    
448                            AssetRendererFactory assetRendererFactory =
449                                    AssetRendererFactoryRegistryUtil.
450                                            getAssetRendererFactoryByClassName(
451                                                    assetEntry.getClassName());
452    
453                            AssetRenderer assetRenderer = assetRendererFactory.getAssetRenderer(
454                                    assetEntry.getClassPK());
455    
456                            if (!assetRendererFactory.isActive(
457                                            permissionChecker.getCompanyId())) {
458    
459                                    if (deleteMissingAssetEntries) {
460                                            missingAssetEntryUuids.add(assetEntryUuid);
461                                    }
462    
463                                    continue;
464                            }
465    
466                            if (checkPermission &&
467                                    (!assetRenderer.isDisplayable() ||
468                                     !assetRenderer.hasViewPermission(permissionChecker))) {
469    
470                                    continue;
471                            }
472    
473                            assetEntries.add(assetEntry);
474                    }
475    
476                    if (deleteMissingAssetEntries) {
477                            AssetPublisherUtil.removeAndStoreSelection(
478                                    missingAssetEntryUuids, portletPreferences);
479    
480                            if (!missingAssetEntryUuids.isEmpty()) {
481                                    SessionMessages.add(
482                                            portletRequest, "deletedMissingAssetEntries",
483                                            missingAssetEntryUuids);
484                            }
485                    }
486    
487                    return assetEntries;
488            }
489    
490            @Override
491            public AssetEntryQuery getAssetEntryQuery(
492                            PortletPreferences portletPreferences, long[] scopeGroupIds)
493                    throws PortalException, SystemException {
494    
495                    AssetEntryQuery assetEntryQuery = new AssetEntryQuery();
496    
497                    long[] allAssetCategoryIds = new long[0];
498                    long[] anyAssetCategoryIds = new long[0];
499                    long[] notAllAssetCategoryIds = new long[0];
500                    long[] notAnyAssetCategoryIds = new long[0];
501    
502                    String[] allAssetTagNames = new String[0];
503                    String[] anyAssetTagNames = new String[0];
504                    String[] notAllAssetTagNames = new String[0];
505                    String[] notAnyAssetTagNames = new String[0];
506    
507                    for (int i = 0; true; i++) {
508                            String[] queryValues = portletPreferences.getValues(
509                                    "queryValues" + i, null);
510    
511                            if (ArrayUtil.isEmpty(queryValues)) {
512                                    break;
513                            }
514    
515                            boolean queryContains = GetterUtil.getBoolean(
516                                    portletPreferences.getValue(
517                                            "queryContains" + i, StringPool.BLANK));
518                            boolean queryAndOperator = GetterUtil.getBoolean(
519                                    portletPreferences.getValue(
520                                            "queryAndOperator" + i, StringPool.BLANK));
521                            String queryName = portletPreferences.getValue(
522                                    "queryName" + i, StringPool.BLANK);
523    
524                            if (Validator.equals(queryName, "assetCategories")) {
525                                    long[] assetCategoryIds = GetterUtil.getLongValues(queryValues);
526    
527                                    if (queryContains && queryAndOperator) {
528                                            allAssetCategoryIds = assetCategoryIds;
529                                    }
530                                    else if (queryContains && !queryAndOperator) {
531                                            anyAssetCategoryIds = assetCategoryIds;
532                                    }
533                                    else if (!queryContains && queryAndOperator) {
534                                            notAllAssetCategoryIds = assetCategoryIds;
535                                    }
536                                    else {
537                                            notAnyAssetCategoryIds = assetCategoryIds;
538                                    }
539                            }
540                            else {
541                                    if (queryContains && queryAndOperator) {
542                                            allAssetTagNames = queryValues;
543                                    }
544                                    else if (queryContains && !queryAndOperator) {
545                                            anyAssetTagNames = queryValues;
546                                    }
547                                    else if (!queryContains && queryAndOperator) {
548                                            notAllAssetTagNames = queryValues;
549                                    }
550                                    else {
551                                            notAnyAssetTagNames = queryValues;
552                                    }
553                            }
554                    }
555    
556                    assetEntryQuery.setAllCategoryIds(allAssetCategoryIds);
557    
558                    for (String assetTagName : allAssetTagNames) {
559                            long[] allAssetTagIds = AssetTagLocalServiceUtil.getTagIds(
560                                    scopeGroupIds, assetTagName);
561    
562                            assetEntryQuery.addAllTagIdsArray(allAssetTagIds);
563                    }
564    
565                    assetEntryQuery.setAnyCategoryIds(anyAssetCategoryIds);
566    
567                    long[] anyAssetTagIds = AssetTagLocalServiceUtil.getTagIds(
568                            scopeGroupIds, anyAssetTagNames);
569    
570                    assetEntryQuery.setAnyTagIds(anyAssetTagIds);
571    
572                    assetEntryQuery.setNotAllCategoryIds(notAllAssetCategoryIds);
573    
574                    for (String assetTagName : notAllAssetTagNames) {
575                            long[] notAllAssetTagIds = AssetTagLocalServiceUtil.getTagIds(
576                                    scopeGroupIds, assetTagName);
577    
578                            assetEntryQuery.addNotAllTagIdsArray(notAllAssetTagIds);
579                    }
580    
581                    assetEntryQuery.setNotAnyCategoryIds(notAnyAssetCategoryIds);
582    
583                    long[] notAnyAssetTagIds = AssetTagLocalServiceUtil.getTagIds(
584                            scopeGroupIds, notAnyAssetTagNames);
585    
586                    assetEntryQuery.setNotAnyTagIds(notAnyAssetTagIds);
587    
588                    return assetEntryQuery;
589            }
590    
591            @Override
592            public String[] getAssetTagNames(
593                            PortletPreferences portletPreferences, long scopeGroupId)
594                    throws Exception {
595    
596                    String[] allAssetTagNames = new String[0];
597    
598                    for (int i = 0; true; i++) {
599                            String[] queryValues = portletPreferences.getValues(
600                                    "queryValues" + i, null);
601    
602                            if (ArrayUtil.isEmpty(queryValues)) {
603                                    break;
604                            }
605    
606                            boolean queryContains = GetterUtil.getBoolean(
607                                    portletPreferences.getValue(
608                                            "queryContains" + i, StringPool.BLANK));
609                            boolean queryAndOperator = GetterUtil.getBoolean(
610                                    portletPreferences.getValue(
611                                            "queryAndOperator" + i, StringPool.BLANK));
612                            String queryName = portletPreferences.getValue(
613                                    "queryName" + i, StringPool.BLANK);
614    
615                            if (!Validator.equals(queryName, "assetCategories") &&
616                                    queryContains && queryAndOperator) {
617    
618                                    allAssetTagNames = queryValues;
619                            }
620                    }
621    
622                    return allAssetTagNames;
623            }
624    
625            @Override
626            public String getClassName(AssetRendererFactory assetRendererFactory) {
627                    Class<?> clazz = assetRendererFactory.getClass();
628    
629                    String className = clazz.getName();
630    
631                    int pos = className.lastIndexOf(StringPool.PERIOD);
632    
633                    return className.substring(pos + 1);
634            }
635    
636            @Override
637            public long[] getClassNameIds(
638                    PortletPreferences portletPreferences, long[] availableClassNameIds) {
639    
640                    boolean anyAssetType = GetterUtil.getBoolean(
641                            portletPreferences.getValue(
642                                    "anyAssetType", Boolean.TRUE.toString()));
643                    String selectionStyle = portletPreferences.getValue(
644                            "selectionStyle", "dynamic");
645    
646                    if (anyAssetType || selectionStyle.equals("manual")) {
647                            return availableClassNameIds;
648                    }
649    
650                    long defaultClassNameId = GetterUtil.getLong(
651                            portletPreferences.getValue("anyAssetType", null));
652    
653                    if (defaultClassNameId > 0) {
654                            return new long[] {defaultClassNameId};
655                    }
656    
657                    long[] classNameIds = GetterUtil.getLongValues(
658                            portletPreferences.getValues("classNameIds", null));
659    
660                    if (ArrayUtil.isNotEmpty(classNameIds)) {
661                            return classNameIds;
662                    }
663                    else {
664                            return availableClassNameIds;
665                    }
666            }
667    
668            @Override
669            public Long[] getClassTypeIds(
670                    PortletPreferences portletPreferences, String className,
671                    Long[] availableClassTypeIds) {
672    
673                    boolean anyAssetType = GetterUtil.getBoolean(
674                            portletPreferences.getValue(
675                                    "anyClassType" + className, Boolean.TRUE.toString()));
676    
677                    if (anyAssetType) {
678                            return availableClassTypeIds;
679                    }
680    
681                    long defaultClassTypeId = GetterUtil.getLong(
682                            portletPreferences.getValue("anyClassType" + className, null));
683    
684                    if (defaultClassTypeId > 0) {
685                            return new Long[] {defaultClassTypeId};
686                    }
687    
688                    Long[] classTypeIds = ArrayUtil.toArray(
689                            StringUtil.split(
690                                    portletPreferences.getValue(
691                                            "classTypeIds" + className, null), 0L));
692    
693                    if (classTypeIds != null) {
694                            return classTypeIds;
695                    }
696                    else {
697                            return availableClassTypeIds;
698                    }
699            }
700    
701            @Override
702            public Map<Locale, String> getEmailAssetEntryAddedBodyMap(
703                    PortletPreferences preferences) {
704    
705                    Map<Locale, String> map = LocalizationUtil.getLocalizationMap(
706                            preferences, "emailAssetEntryAddedBody");
707    
708                    Locale defaultLocale = LocaleUtil.getSiteDefault();
709    
710                    String defaultValue = map.get(defaultLocale);
711    
712                    if (Validator.isNotNull(defaultValue)) {
713                            return map;
714                    }
715    
716                    map.put(
717                            defaultLocale,
718                            ContentUtil.get(
719                                    PropsUtil.get(
720                                            PropsKeys.ASSET_PUBLISHER_EMAIL_ASSET_ENTRY_ADDED_BODY)));
721    
722                    return map;
723            }
724    
725            @Override
726            public boolean getEmailAssetEntryAddedEnabled(
727                    PortletPreferences preferences) {
728    
729                    String emailAssetEntryAddedEnabled = preferences.getValue(
730                            "emailAssetEntryAddedEnabled", StringPool.BLANK);
731    
732                    if (Validator.isNotNull(emailAssetEntryAddedEnabled)) {
733                            return GetterUtil.getBoolean(emailAssetEntryAddedEnabled);
734                    }
735                    else {
736                            return PropsValues.ASSET_PUBLISHER_EMAIL_ASSET_ENTRY_ADDED_ENABLED;
737                    }
738            }
739    
740            @Override
741            public Map<Locale, String> getEmailAssetEntryAddedSubjectMap(
742                    PortletPreferences preferences) {
743    
744                    Map<Locale, String> map = LocalizationUtil.getLocalizationMap(
745                            preferences, "emailAssetEntryAddedSubject");
746    
747                    Locale defaultLocale = LocaleUtil.getSiteDefault();
748    
749                    String defaultValue = map.get(defaultLocale);
750    
751                    if (Validator.isNotNull(defaultValue)) {
752                            return map;
753                    }
754    
755                    map.put(
756                            defaultLocale,
757                            ContentUtil.get(
758                                    PropsUtil.get(
759                                            PropsKeys.ASSET_PUBLISHER_EMAIL_ASSET_ENTRY_ADDED_SUBJECT))
760                    );
761    
762                    return map;
763            }
764    
765            @Override
766            public String getEmailFromAddress(
767                            PortletPreferences preferences, long companyId)
768                    throws SystemException {
769    
770                    return PortalUtil.getEmailFromAddress(
771                            preferences, companyId,
772                            PropsValues.ASSET_PUBLISHER_EMAIL_FROM_ADDRESS);
773            }
774    
775            @Override
776            public String getEmailFromName(
777                            PortletPreferences preferences, long companyId)
778                    throws SystemException {
779    
780                    return PortalUtil.getEmailFromName(
781                            preferences, companyId,
782                            PropsValues.ASSET_PUBLISHER_EMAIL_FROM_NAME);
783            }
784    
785            @Override
786            public long getGroupIdFromScopeId(
787                            String scopeId, long siteGroupId, boolean privateLayout)
788                    throws PortalException, SystemException {
789    
790                    if (scopeId.startsWith(SCOPE_ID_CHILD_GROUP_PREFIX)) {
791                            String scopeIdSuffix = scopeId.substring(
792                                    SCOPE_ID_CHILD_GROUP_PREFIX.length());
793    
794                            return GetterUtil.getLong(scopeIdSuffix);
795                    }
796                    else if (scopeId.startsWith(SCOPE_ID_GROUP_PREFIX)) {
797                            String scopeIdSuffix = scopeId.substring(
798                                    SCOPE_ID_GROUP_PREFIX.length());
799    
800                            if (scopeIdSuffix.equals(GroupConstants.DEFAULT)) {
801                                    return siteGroupId;
802                            }
803    
804                            return GetterUtil.getLong(scopeIdSuffix);
805                    }
806                    else if (scopeId.startsWith(SCOPE_ID_LAYOUT_UUID_PREFIX)) {
807                            String layoutUuid = scopeId.substring(
808                                    SCOPE_ID_LAYOUT_UUID_PREFIX.length());
809    
810                            Layout scopeIdLayout =
811                                    LayoutLocalServiceUtil.getLayoutByUuidAndGroupId(
812                                            layoutUuid, siteGroupId, privateLayout);
813    
814                            Group scopeIdGroup = null;
815    
816                            if (scopeIdLayout.hasScopeGroup()) {
817                                    scopeIdGroup = scopeIdLayout.getScopeGroup();
818                            }
819                            else {
820                                    scopeIdGroup = GroupLocalServiceUtil.addGroup(
821                                            PrincipalThreadLocal.getUserId(),
822                                            GroupConstants.DEFAULT_PARENT_GROUP_ID,
823                                            Layout.class.getName(), scopeIdLayout.getPlid(),
824                                            GroupConstants.DEFAULT_LIVE_GROUP_ID,
825                                            String.valueOf(scopeIdLayout.getPlid()), null, 0, true,
826                                            GroupConstants.DEFAULT_MEMBERSHIP_RESTRICTION, null, false,
827                                            true, null);
828                            }
829    
830                            return scopeIdGroup.getGroupId();
831                    }
832                    else if (scopeId.startsWith(SCOPE_ID_LAYOUT_PREFIX)) {
833    
834                            // Legacy preferences
835    
836                            String scopeIdSuffix = scopeId.substring(
837                                    SCOPE_ID_LAYOUT_PREFIX.length());
838    
839                            long scopeIdLayoutId = GetterUtil.getLong(scopeIdSuffix);
840    
841                            Layout scopeIdLayout = LayoutLocalServiceUtil.getLayout(
842                                    siteGroupId, privateLayout, scopeIdLayoutId);
843    
844                            Group scopeIdGroup = scopeIdLayout.getScopeGroup();
845    
846                            return scopeIdGroup.getGroupId();
847                    }
848                    else if (scopeId.startsWith(SCOPE_ID_PARENT_GROUP_PREFIX)) {
849                            String scopeIdSuffix = scopeId.substring(
850                                    SCOPE_ID_PARENT_GROUP_PREFIX.length());
851    
852                            return GetterUtil.getLong(scopeIdSuffix);
853                    }
854                    else {
855                            throw new IllegalArgumentException("Invalid scope ID " + scopeId);
856                    }
857            }
858    
859            @Override
860            public long[] getGroupIds(
861                    PortletPreferences portletPreferences, long scopeGroupId,
862                    Layout layout) {
863    
864                    String[] scopeIds = portletPreferences.getValues(
865                            "scopeIds", new String[] {SCOPE_ID_GROUP_PREFIX + scopeGroupId});
866    
867                    long[] groupIds = new long[scopeIds.length];
868    
869                    int i = 0;
870    
871                    for (String scopeId : scopeIds) {
872                            try {
873                                    groupIds[i] = getGroupIdFromScopeId(
874                                            scopeId, scopeGroupId, layout.isPrivateLayout());
875    
876                                    i++;
877                            }
878                            catch (Exception e) {
879                                    continue;
880                            }
881                    }
882    
883                    return groupIds;
884            }
885    
886            @Override
887            public long getRecentFolderId(
888                    PortletRequest portletRequest, String className) {
889    
890                    Long classPK = _getRecentFolderIds(portletRequest).get(className);
891    
892                    if (classPK == null) {
893                            return 0;
894                    }
895                    else {
896                            return classPK.longValue();
897                    }
898            }
899    
900            @Override
901            public String getScopeId(Group group, long scopeGroupId)
902                    throws PortalException, SystemException {
903    
904                    String key = null;
905    
906                    if (group.isLayout()) {
907                            Layout layout = LayoutLocalServiceUtil.getLayout(
908                                    group.getClassPK());
909    
910                            key = SCOPE_ID_LAYOUT_UUID_PREFIX + layout.getUuid();
911                    }
912                    else if (group.isLayoutPrototype() ||
913                                     (group.getGroupId() == scopeGroupId)) {
914    
915                            key = SCOPE_ID_GROUP_PREFIX + GroupConstants.DEFAULT;
916                    }
917                    else {
918                            Group scopeGroup = GroupLocalServiceUtil.getGroup(scopeGroupId);
919    
920                            if (scopeGroup.hasAncestor(group.getGroupId())) {
921                                    key = SCOPE_ID_PARENT_GROUP_PREFIX + group.getGroupId();
922                            }
923                            else if (group.hasAncestor(scopeGroup.getGroupId())) {
924                                    key = SCOPE_ID_CHILD_GROUP_PREFIX + group.getGroupId();
925                            }
926                            else {
927                                    key = SCOPE_ID_GROUP_PREFIX + group.getGroupId();
928                            }
929                    }
930    
931                    return key;
932            }
933    
934            @Override
935            public boolean isScopeIdSelectable(
936                            PermissionChecker permissionChecker, String scopeId,
937                            long companyGroupId, Layout layout)
938                    throws PortalException, SystemException {
939    
940                    long groupId = getGroupIdFromScopeId(
941                            scopeId, layout.getGroupId(), layout.isPrivateLayout());
942    
943                    if (scopeId.startsWith(SCOPE_ID_CHILD_GROUP_PREFIX)) {
944                            Group group = GroupLocalServiceUtil.getGroup(groupId);
945    
946                            if (!group.hasAncestor(layout.getGroupId())) {
947                                    return false;
948                            }
949                    }
950                    else if (scopeId.startsWith(SCOPE_ID_PARENT_GROUP_PREFIX)) {
951                            Group siteGroup = layout.getGroup();
952    
953                            if (!siteGroup.hasAncestor(groupId)) {
954                                    return false;
955                            }
956    
957                            Group group = GroupLocalServiceUtil.getGroup(groupId);
958    
959                            if (SitesUtil.isContentSharingWithChildrenEnabled(group)) {
960                                    return true;
961                            }
962    
963                            if (!PrefsPropsUtil.getBoolean(
964                                            layout.getCompanyId(),
965                                            PropsKeys.
966                                            SITES_CONTENT_SHARING_THROUGH_ADMINISTRATORS_ENABLED)) {
967    
968                                    return false;
969                            }
970    
971                            return GroupPermissionUtil.contains(
972                                    permissionChecker, groupId, ActionKeys.UPDATE);
973                    }
974                    else if (groupId != companyGroupId) {
975                            return GroupPermissionUtil.contains(
976                                    permissionChecker, groupId, ActionKeys.UPDATE);
977                    }
978    
979                    return true;
980            }
981    
982            @Override
983            public boolean isSubscribed(
984                            long companyId, long userId, long plid, String portletId)
985                    throws PortalException, SystemException {
986    
987                    return SubscriptionLocalServiceUtil.isSubscribed(
988                            companyId, userId,
989                            com.liferay.portal.model.PortletPreferences.class.getName(),
990                            _getPortletPreferencesId(plid, portletId));
991            }
992    
993            @Override
994            public void notifySubscribers(
995                            PortletPreferences preferences, long plid, String portletId,
996                            List<AssetEntry> assetEntries)
997                    throws PortalException, SystemException {
998    
999                    if (!getEmailAssetEntryAddedEnabled(preferences) ||
1000                            assetEntries.isEmpty()) {
1001    
1002                            return;
1003                    }
1004    
1005                    AssetEntry assetEntry = assetEntries.get(0);
1006    
1007                    String fromName = getEmailFromName(
1008                            preferences, assetEntry.getCompanyId());
1009                    String fromAddress = getEmailFromAddress(
1010                            preferences, assetEntry.getCompanyId());
1011    
1012                    Map<Locale, String> localizedSubjectMap =
1013                            getEmailAssetEntryAddedSubjectMap(preferences);
1014                    Map<Locale, String> localizedBodyMap = getEmailAssetEntryAddedBodyMap(
1015                            preferences);
1016    
1017                    SubscriptionSender subscriptionSender = new SubscriptionSender();
1018    
1019                    subscriptionSender.setCompanyId(assetEntry.getCompanyId());
1020                    subscriptionSender.setContextAttributes(
1021                            "[$ASSET_ENTRIES$]",
1022                            ListUtil.toString(
1023                                    assetEntries, _titleAccessor, StringPool.COMMA_AND_SPACE));
1024                    subscriptionSender.setContextUserPrefix("ASSET_PUBLISHER");
1025                    subscriptionSender.setFrom(fromAddress, fromName);
1026                    subscriptionSender.setHtmlFormat(true);
1027                    subscriptionSender.setLocalizedBodyMap(localizedBodyMap);
1028                    subscriptionSender.setLocalizedSubjectMap(localizedSubjectMap);
1029                    subscriptionSender.setMailId("asset_entry", assetEntry.getEntryId());
1030                    subscriptionSender.setPortletId(PortletKeys.ASSET_PUBLISHER);
1031                    subscriptionSender.setReplyToAddress(fromAddress);
1032    
1033                    subscriptionSender.addPersistedSubscribers(
1034                            com.liferay.portal.model.PortletPreferences.class.getName(),
1035                            _getPortletPreferencesId(plid, portletId));
1036    
1037                    subscriptionSender.flushNotificationsAsync();
1038            }
1039    
1040            @Override
1041            public void removeAndStoreSelection(
1042                            List<String> assetEntryUuids, PortletPreferences portletPreferences)
1043                    throws Exception {
1044    
1045                    if (assetEntryUuids.size() == 0) {
1046                            return;
1047                    }
1048    
1049                    String[] assetEntryXmls = portletPreferences.getValues(
1050                            "assetEntryXml", new String[0]);
1051    
1052                    List<String> assetEntryXmlsList = ListUtil.fromArray(assetEntryXmls);
1053    
1054                    Iterator<String> itr = assetEntryXmlsList.iterator();
1055    
1056                    while (itr.hasNext()) {
1057                            String assetEntryXml = itr.next();
1058    
1059                            Document document = SAXReaderUtil.read(assetEntryXml);
1060    
1061                            Element rootElement = document.getRootElement();
1062    
1063                            String assetEntryUuid = rootElement.elementText("asset-entry-uuid");
1064    
1065                            if (assetEntryUuids.contains(assetEntryUuid)) {
1066                                    itr.remove();
1067                            }
1068                    }
1069    
1070                    portletPreferences.setValues(
1071                            "assetEntryXml",
1072                            assetEntryXmlsList.toArray(new String[assetEntryXmlsList.size()]));
1073    
1074                    portletPreferences.store();
1075            }
1076    
1077            @Override
1078            public void removeRecentFolderId(
1079                    PortletRequest portletRequest, String className, long classPK) {
1080    
1081                    if (getRecentFolderId(portletRequest, className) == classPK) {
1082                            _getRecentFolderIds(portletRequest).remove(className);
1083                    }
1084            }
1085    
1086            @Override
1087            public void subscribe(
1088                            PermissionChecker permissionChecker, long groupId, long plid,
1089                            String portletId)
1090                    throws PortalException, SystemException {
1091    
1092                    PortletPermissionUtil.check(
1093                            permissionChecker, plid, portletId, ActionKeys.SUBSCRIBE);
1094    
1095                    SubscriptionLocalServiceUtil.addSubscription(
1096                            permissionChecker.getUserId(), groupId,
1097                            com.liferay.portal.model.PortletPreferences.class.getName(),
1098                            _getPortletPreferencesId(plid, portletId));
1099            }
1100    
1101            @Override
1102            public void unsubscribe(
1103                            PermissionChecker permissionChecker, long plid, String portletId)
1104                    throws PortalException, SystemException {
1105    
1106                    PortletPermissionUtil.check(
1107                            permissionChecker, plid, portletId, ActionKeys.SUBSCRIBE);
1108    
1109                    SubscriptionLocalServiceUtil.deleteSubscription(
1110                            permissionChecker.getUserId(),
1111                            com.liferay.portal.model.PortletPreferences.class.getName(),
1112                            _getPortletPreferencesId(plid, portletId));
1113            }
1114    
1115            private void _checkAssetEntries(
1116                            com.liferay.portal.model.PortletPreferences portletPreferences)
1117                    throws PortalException, SystemException {
1118    
1119                    Layout layout = LayoutLocalServiceUtil.getLayout(
1120                            portletPreferences.getPlid());
1121    
1122                    PortletPreferences preferences = PortletPreferencesFactoryUtil.fromXML(
1123                            layout.getCompanyId(), portletPreferences.getOwnerId(),
1124                            portletPreferences.getOwnerType(), portletPreferences.getPlid(),
1125                            portletPreferences.getPortletId(),
1126                            portletPreferences.getPreferences());
1127    
1128                    if (!getEmailAssetEntryAddedEnabled(preferences)) {
1129                            return;
1130                    }
1131    
1132                    List<AssetEntry> assetEntries = getAssetEntries(
1133                            preferences, layout, layout.getGroupId(),
1134                            PropsValues.ASSET_PUBLISHER_DYNAMIC_SUBSCRIPTION_LIMIT, false);
1135    
1136                    if (assetEntries.isEmpty()) {
1137                            return;
1138                    }
1139    
1140                    long[] notifiedAssetEntryIds = GetterUtil.getLongValues(
1141                            preferences.getValues("notifiedAssetEntryIds", null));
1142    
1143                    List<AssetEntry> newAssetEntries = new ArrayList<AssetEntry>();
1144    
1145                    for (int i = 0; i < assetEntries.size(); i++) {
1146                            AssetEntry assetEntry = assetEntries.get(i);
1147    
1148                            if (!ArrayUtil.contains(
1149                                            notifiedAssetEntryIds, assetEntry.getEntryId())) {
1150    
1151                                    newAssetEntries.add(assetEntry);
1152                            }
1153                    }
1154    
1155                    notifySubscribers(
1156                            preferences, portletPreferences.getPlid(),
1157                            portletPreferences.getPortletId(), newAssetEntries);
1158    
1159                    try {
1160                            preferences.setValues(
1161                                    "notifiedAssetEntryIds",
1162                                    StringUtil.split(
1163                                            ListUtil.toString(
1164                                                    assetEntries, AssetEntry.ENTRY_ID_ACCESSOR)));
1165    
1166                            preferences.store();
1167                    }
1168                    catch (IOException ioe) {
1169                            throw new SystemException(ioe);
1170                    }
1171                    catch (PortletException pe) {
1172                            throw new SystemException(pe);
1173                    }
1174            }
1175    
1176            private List<AssetEntry> _filterAssetCategoriesAssetEntries(
1177                            List<AssetEntry> assetEntries, long[] assetCategoryIds)
1178                    throws Exception {
1179    
1180                    List<AssetEntry> filteredAssetEntries = new ArrayList<AssetEntry>();
1181    
1182                    for (AssetEntry assetEntry : assetEntries) {
1183                            if (ArrayUtil.containsAll(
1184                                            assetEntry.getCategoryIds(), assetCategoryIds)) {
1185    
1186                                    filteredAssetEntries.add(assetEntry);
1187                            }
1188                    }
1189    
1190                    return filteredAssetEntries;
1191            }
1192    
1193            private List<AssetEntry> _filterAssetTagNamesAssetEntries(
1194                            List<AssetEntry> assetEntries, String[] assetTagNames)
1195                    throws Exception {
1196    
1197                    List<AssetEntry> filteredAssetEntries = new ArrayList<AssetEntry>();
1198    
1199                    for (AssetEntry assetEntry : assetEntries) {
1200                            List<AssetTag> assetTags = assetEntry.getTags();
1201    
1202                            String[] assetEntryAssetTagNames = new String[assetTags.size()];
1203    
1204                            for (int i = 0; i < assetTags.size(); i++) {
1205                                    AssetTag assetTag = assetTags.get(i);
1206    
1207                                    assetEntryAssetTagNames[i] = assetTag.getName();
1208                            }
1209    
1210                            if (ArrayUtil.containsAll(assetEntryAssetTagNames, assetTagNames)) {
1211                                    filteredAssetEntries.add(assetEntry);
1212                            }
1213                    }
1214    
1215                    return filteredAssetEntries;
1216            }
1217    
1218            private String _getAssetEntryXml(
1219                    String assetEntryType, String assetEntryUuid) {
1220    
1221                    String xml = null;
1222    
1223                    try {
1224                            Document document = SAXReaderUtil.createDocument(StringPool.UTF8);
1225    
1226                            Element assetEntryElement = document.addElement("asset-entry");
1227    
1228                            Element assetEntryTypeElement = assetEntryElement.addElement(
1229                                    "asset-entry-type");
1230    
1231                            assetEntryTypeElement.addText(assetEntryType);
1232    
1233                            Element assetEntryUuidElement = assetEntryElement.addElement(
1234                                    "asset-entry-uuid");
1235    
1236                            assetEntryUuidElement.addText(assetEntryUuid);
1237    
1238                            xml = document.formattedString(StringPool.BLANK);
1239                    }
1240                    catch (IOException ioe) {
1241                            if (_log.isWarnEnabled()) {
1242                                    _log.warn(ioe);
1243                            }
1244                    }
1245    
1246                    return xml;
1247            }
1248    
1249            private long _getPortletPreferencesId(long plid, String portletId)
1250                    throws PortalException, SystemException {
1251    
1252                    com.liferay.portal.model.PortletPreferences portletPreferences =
1253                            PortletPreferencesLocalServiceUtil.getPortletPreferences(
1254                                    PortletKeys.PREFS_OWNER_ID_DEFAULT,
1255                                    PortletKeys.PREFS_OWNER_TYPE_LAYOUT, plid, portletId);
1256    
1257                    return portletPreferences.getPortletPreferencesId();
1258            }
1259    
1260            private Map<String, Long> _getRecentFolderIds(
1261                    PortletRequest portletRequest) {
1262    
1263                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
1264                            portletRequest);
1265                    HttpSession session = request.getSession();
1266    
1267                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
1268                            WebKeys.THEME_DISPLAY);
1269    
1270                    String key =
1271                            AssetPublisherUtil.class + StringPool.UNDERLINE +
1272                                    themeDisplay.getScopeGroupId();
1273    
1274                    Map<String, Long> recentFolderIds =
1275                            (Map<String, Long>)session.getAttribute(key);
1276    
1277                    if (recentFolderIds == null) {
1278                            recentFolderIds = new HashMap<String, Long>();
1279                    }
1280    
1281                    session.setAttribute(key, recentFolderIds);
1282    
1283                    return recentFolderIds;
1284            }
1285    
1286            private static Log _log = LogFactoryUtil.getLog(AssetPublisherImpl.class);
1287    
1288            private Accessor<AssetEntry, String> _titleAccessor =
1289                    new Accessor<AssetEntry, String>() {
1290    
1291                            @Override
1292                            public String get(AssetEntry assetEntry) {
1293                                    return assetEntry.getTitle(LocaleUtil.getSiteDefault());
1294                            }
1295    
1296                    };
1297    
1298    }