001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.convert.documentlibrary;
016    
017    import com.liferay.portal.convert.BaseConvertProcess;
018    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
019    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
020    import com.liferay.portal.kernel.dao.orm.Property;
021    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
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.repository.model.FileEntry;
027    import com.liferay.portal.kernel.repository.model.FileVersion;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.ListUtil;
030    import com.liferay.portal.kernel.util.PropsKeys;
031    import com.liferay.portal.kernel.util.StringBundler;
032    import com.liferay.portal.kernel.util.StringPool;
033    import com.liferay.portal.kernel.workflow.WorkflowConstants;
034    import com.liferay.portal.model.Image;
035    import com.liferay.portal.repository.liferayrepository.model.LiferayFileEntry;
036    import com.liferay.portal.service.ImageLocalServiceUtil;
037    import com.liferay.portal.util.MaintenanceUtil;
038    import com.liferay.portal.util.PropsValues;
039    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
040    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
041    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
042    import com.liferay.portlet.documentlibrary.store.Store;
043    import com.liferay.portlet.documentlibrary.store.StoreFactory;
044    import com.liferay.portlet.documentlibrary.util.DLPreviewableProcessor;
045    import com.liferay.portlet.documentlibrary.util.comparator.FileVersionVersionComparator;
046    import com.liferay.portlet.messageboards.model.MBMessage;
047    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
048    import com.liferay.registry.Registry;
049    import com.liferay.registry.RegistryUtil;
050    
051    import java.io.InputStream;
052    
053    import java.util.Collection;
054    import java.util.List;
055    
056    /**
057     * @author Minhchau Dang
058     * @author Alexander Chow
059     * @author L??szl?? Csontos
060     */
061    public class DocumentLibraryConvertProcess
062            extends BaseConvertProcess implements DLStoreConverter {
063    
064            @Override
065            public String getConfigurationErrorMessage() {
066                    return "there-are-no-stores-configured";
067            }
068    
069            @Override
070            public String getDescription() {
071                    return "migrate-documents-from-one-repository-to-another";
072            }
073    
074            @Override
075            public String getParameterDescription() {
076                    return "please-select-a-new-repository-hook";
077            }
078    
079            @Override
080            public String[] getParameterNames() {
081                    StoreFactory storeFactory = StoreFactory.getInstance();
082    
083                    Store store = storeFactory.getStore();
084    
085                    if (store == null) {
086                            return null;
087                    }
088    
089                    String[] storeTypes = storeFactory.getStoreTypes();
090    
091                    StringBundler sb = new StringBundler(storeTypes.length * 2 + 2);
092    
093                    sb.append(PropsKeys.DL_STORE_IMPL);
094                    sb.append(StringPool.EQUAL);
095    
096                    for (String storeType : storeTypes) {
097                            Class<?> clazz = store.getClass();
098    
099                            if (!storeType.equals(clazz.getName())) {
100                                    sb.append(storeType);
101                                    sb.append(StringPool.SEMICOLON);
102                            }
103                    }
104    
105                    return new String[] {
106                            sb.toString(), "delete-files-from-previous-repository=checkbox"
107                    };
108            }
109    
110            @Override
111            public boolean isEnabled() {
112                    return true;
113            }
114    
115            @Override
116            public void migrateDLFileEntry(
117                    long companyId, long repositoryId, FileEntry fileEntry) {
118    
119                    Object model = fileEntry.getModel();
120    
121                    if (!(model instanceof DLFileEntry)) {
122                            throw new IllegalArgumentException(
123                                    "Unsupported file entry model " + model.getClass());
124                    }
125    
126                    String fileName = ((DLFileEntry)model).getName();
127    
128                    List<FileVersion> fileVersions = getFileVersions(fileEntry);
129    
130                    if (fileVersions.isEmpty()) {
131                            String versionNumber = Store.VERSION_DEFAULT;
132    
133                            migrateFile(companyId, repositoryId, fileName, versionNumber);
134    
135                            return;
136                    }
137    
138                    for (FileVersion fileVersion : fileVersions) {
139                            String versionNumber = fileVersion.getVersion();
140    
141                            migrateFile(companyId, repositoryId, fileName, versionNumber);
142                    }
143            }
144    
145            @Override
146            public void validate() {
147                    String sourceStoreClassName = getSourceStoreClassName();
148    
149                    if (!sourceStoreClassName.endsWith(_FILE_SYSTEM_STORE_SUFFIX)) {
150                            return;
151                    }
152    
153                    String targetStoreClassName = getTargetStoreClassName();
154    
155                    if (!targetStoreClassName.endsWith(_FILE_SYSTEM_STORE_SUFFIX)) {
156                            return;
157                    }
158            }
159    
160            @Override
161            protected void doConvert() throws Exception {
162                    StoreFactory storeFactory = StoreFactory.getInstance();
163    
164                    _sourceStore = storeFactory.getStore();
165    
166                    String targetStoreClassName = getTargetStoreClassName();
167    
168                    _targetStore = storeFactory.getStore(targetStoreClassName);
169    
170                    migratePortlets();
171    
172                    storeFactory.setStore(targetStoreClassName);
173    
174                    MaintenanceUtil.appendStatus(
175                            "Please set " + PropsKeys.DL_STORE_IMPL +
176                                    " in your portal-ext.properties to use " +
177                                            targetStoreClassName);
178    
179                    PropsValues.DL_STORE_IMPL = targetStoreClassName;
180            }
181    
182            protected List<FileVersion> getFileVersions(FileEntry fileEntry) {
183                    List<FileVersion> fileVersions = fileEntry.getFileVersions(
184                            WorkflowConstants.STATUS_ANY);
185    
186                    return ListUtil.sort(
187                            fileVersions, new FileVersionVersionComparator(true));
188            }
189    
190            protected String getSourceStoreClassName() {
191                    StoreFactory storeFactory = StoreFactory.getInstance();
192    
193                    Store sourceStore = storeFactory.getStore();
194    
195                    return sourceStore.getClass().getName();
196            }
197    
198            protected String getTargetStoreClassName() {
199                    String[] values = getParameterValues();
200    
201                    return values[0];
202            }
203    
204            protected boolean isDeleteFilesFromSourceStore() {
205                    String[] values = getParameterValues();
206    
207                    return GetterUtil.getBoolean(values[1]);
208            }
209    
210            protected void migrateDL() throws PortalException {
211                    int count = DLFileEntryLocalServiceUtil.getFileEntriesCount();
212    
213                    MaintenanceUtil.appendStatus(
214                            "Migrating " + count + " documents and media files");
215    
216                    ActionableDynamicQuery actionableDynamicQuery =
217                            DLFileEntryLocalServiceUtil.getActionableDynamicQuery();
218    
219                    actionableDynamicQuery.setAddCriteriaMethod(
220                            new ActionableDynamicQuery.AddCriteriaMethod() {
221    
222                                    @Override
223                                    public void addCriteria(DynamicQuery dynamicQuery) {
224                                            Property classNameIdProperty = PropertyFactoryUtil.forName(
225                                                    "classNameId");
226    
227                                            dynamicQuery.add(classNameIdProperty.eq(0L));
228                                    }
229    
230                            });
231                    actionableDynamicQuery.setPerformActionMethod(
232                            new ActionableDynamicQuery.PerformActionMethod<DLFileEntry>() {
233    
234                                    @Override
235                                    public void performAction(DLFileEntry dlFileEntry) {
236                                            migrateDLFileEntry(
237                                                    dlFileEntry.getCompanyId(),
238                                                    dlFileEntry.getDataRepositoryId(),
239                                                    new LiferayFileEntry(dlFileEntry));
240                                    }
241    
242                            });
243    
244                    actionableDynamicQuery.performActions();
245    
246                    if (isDeleteFilesFromSourceStore()) {
247                            DLPreviewableProcessor.deleteFiles();
248                    }
249            }
250    
251            protected void migrateFile(
252                    long companyId, long repositoryId, String fileName,
253                    String versionNumber) {
254    
255                    try {
256                            InputStream is = _sourceStore.getFileAsStream(
257                                    companyId, repositoryId, fileName, versionNumber);
258    
259                            if (versionNumber.equals(Store.VERSION_DEFAULT)) {
260                                    _targetStore.addFile(companyId, repositoryId, fileName, is);
261                            }
262                            else {
263                                    _targetStore.updateFile(
264                                            companyId, repositoryId, fileName, versionNumber, is);
265                            }
266    
267                            if (isDeleteFilesFromSourceStore()) {
268                                    _sourceStore.deleteFile(
269                                            companyId, repositoryId, fileName, versionNumber);
270                            }
271                    }
272                    catch (Exception e) {
273                            _log.error("Migration failed for " + fileName, e);
274                    }
275            }
276    
277            protected void migrateImages() throws PortalException {
278                    int count = ImageLocalServiceUtil.getImagesCount();
279    
280                    MaintenanceUtil.appendStatus("Migrating " + count + " images");
281    
282                    ActionableDynamicQuery actionableDynamicQuery =
283                            ImageLocalServiceUtil.getActionableDynamicQuery();
284    
285                    actionableDynamicQuery.setPerformActionMethod(
286                            new ActionableDynamicQuery.PerformActionMethod<Image>() {
287    
288                                    @Override
289                                    public void performAction(Image image) {
290                                            String fileName =
291                                                    image.getImageId() + StringPool.PERIOD +
292                                                            image.getType();
293    
294                                            migrateFile(0, 0, fileName, Store.VERSION_DEFAULT);
295                                    }
296    
297                            });
298    
299                    actionableDynamicQuery.performActions();
300            }
301    
302            protected void migrateMB() throws PortalException {
303                    int count = MBMessageLocalServiceUtil.getMBMessagesCount();
304    
305                    MaintenanceUtil.appendStatus(
306                            "Migrating message boards attachments in " + count + " messages");
307    
308                    ActionableDynamicQuery actionableDynamicQuery =
309                            MBMessageLocalServiceUtil.getActionableDynamicQuery();
310    
311                    actionableDynamicQuery.setPerformActionMethod(
312                            new ActionableDynamicQuery.PerformActionMethod<MBMessage>() {
313    
314                                    @Override
315                                    public void performAction(MBMessage mbMessage)
316                                            throws PortalException {
317    
318                                            for (FileEntry fileEntry :
319                                                            mbMessage.getAttachmentsFileEntries()) {
320    
321                                                    DLFileEntry dlFileEntry =
322                                                            (DLFileEntry)fileEntry.getModel();
323    
324                                                    migrateDLFileEntry(
325                                                            mbMessage.getCompanyId(),
326                                                            DLFolderConstants.getDataRepositoryId(
327                                                                    dlFileEntry.getRepositoryId(),
328                                                                    dlFileEntry.getFolderId()),
329                                                            new LiferayFileEntry(dlFileEntry));
330                                            }
331                                    }
332    
333                            });
334    
335                    actionableDynamicQuery.performActions();
336            }
337    
338            protected void migratePortlets() throws Exception {
339                    migrateImages();
340                    migrateDL();
341                    migrateMB();
342    
343                    Collection<DLStoreConvertProcess> dlStoreConvertProcesses =
344                            _getDLStoreConvertProcesses();
345    
346                    for (DLStoreConvertProcess dlStoreConvertProcess :
347                                    dlStoreConvertProcesses) {
348    
349                            dlStoreConvertProcess.migrate(this);
350                    }
351            }
352    
353            private Collection<DLStoreConvertProcess> _getDLStoreConvertProcesses() {
354                    try {
355                            Registry registry = RegistryUtil.getRegistry();
356    
357                            return registry.getServices(DLStoreConvertProcess.class, null);
358                    }
359                    catch (Exception e) {
360                            throw new SystemException(e);
361                    }
362            }
363    
364            private static final String _FILE_SYSTEM_STORE_SUFFIX = "FileSystemStore";
365    
366            private static final Log _log = LogFactoryUtil.getLog(
367                    DocumentLibraryConvertProcess.class);
368    
369            private Store _sourceStore;
370            private Store _targetStore;
371    
372    }