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.portal.convert;
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.exception.PortalException;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.repository.model.FileEntry;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.InstanceFactory;
028    import com.liferay.portal.kernel.util.ListUtil;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.workflow.WorkflowConstants;
033    import com.liferay.portal.model.Image;
034    import com.liferay.portal.service.ImageLocalServiceUtil;
035    import com.liferay.portal.service.persistence.ImageActionableDynamicQuery;
036    import com.liferay.portal.util.ClassLoaderUtil;
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.DLFileVersion;
041    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
042    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
043    import com.liferay.portlet.documentlibrary.service.persistence.DLFileEntryActionableDynamicQuery;
044    import com.liferay.portlet.documentlibrary.store.AdvancedFileSystemStore;
045    import com.liferay.portlet.documentlibrary.store.CMISStore;
046    import com.liferay.portlet.documentlibrary.store.DBStore;
047    import com.liferay.portlet.documentlibrary.store.FileSystemStore;
048    import com.liferay.portlet.documentlibrary.store.JCRStore;
049    import com.liferay.portlet.documentlibrary.store.S3Store;
050    import com.liferay.portlet.documentlibrary.store.Store;
051    import com.liferay.portlet.documentlibrary.store.StoreFactory;
052    import com.liferay.portlet.documentlibrary.util.DLPreviewableProcessor;
053    import com.liferay.portlet.documentlibrary.util.comparator.FileVersionVersionComparator;
054    import com.liferay.portlet.messageboards.model.MBMessage;
055    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
056    import com.liferay.portlet.messageboards.service.persistence.MBMessageActionableDynamicQuery;
057    import com.liferay.portlet.wiki.model.WikiPage;
058    import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
059    import com.liferay.portlet.wiki.service.persistence.WikiPageActionableDynamicQuery;
060    
061    import java.io.InputStream;
062    
063    import java.util.List;
064    
065    /**
066     * @author Minhchau Dang
067     * @author Alexander Chow
068     */
069    public class ConvertDocumentLibrary extends ConvertProcess {
070    
071            @Override
072            public String getDescription() {
073                    return "migrate-documents-from-one-repository-to-another";
074            }
075    
076            @Override
077            public String getParameterDescription() {
078                    return "please-select-a-new-repository-hook";
079            }
080    
081            @Override
082            public String[] getParameterNames() {
083                    StringBundler sb = new StringBundler(_HOOKS.length * 2 + 2);
084    
085                    sb.append(PropsKeys.DL_STORE_IMPL);
086                    sb.append(StringPool.EQUAL);
087    
088                    for (String hook : _HOOKS) {
089                            if (!hook.equals(PropsValues.DL_STORE_IMPL)) {
090                                    sb.append(hook);
091                                    sb.append(StringPool.SEMICOLON);
092                            }
093                    }
094    
095                    return new String[] {
096                            sb.toString(), "delete-files-from-previous-repository=checkbox"
097                    };
098            }
099    
100            @Override
101            public boolean isEnabled() {
102                    return true;
103            }
104    
105            @Override
106            protected void doConvert() throws Exception {
107                    _sourceStore = StoreFactory.getInstance();
108    
109                    String[] values = getParameterValues();
110    
111                    String targetStoreClassName = values[0];
112    
113                    _targetStore = (Store)InstanceFactory.newInstance(
114                            ClassLoaderUtil.getPortalClassLoader(), targetStoreClassName);
115    
116                    migratePortlets();
117    
118                    StoreFactory.setInstance(_targetStore);
119    
120                    MaintenanceUtil.appendStatus(
121                            "Please set " + PropsKeys.DL_STORE_IMPL +
122                                    " in your portal-ext.properties to use " +
123                                            targetStoreClassName);
124    
125                    PropsValues.DL_STORE_IMPL = targetStoreClassName;
126            }
127    
128            protected List<DLFileVersion> getDLFileVersions(DLFileEntry dlFileEntry)
129                    throws SystemException {
130    
131                    List<DLFileVersion> dlFileVersions = dlFileEntry.getFileVersions(
132                            WorkflowConstants.STATUS_ANY);
133    
134                    return ListUtil.sort(
135                            dlFileVersions, new FileVersionVersionComparator(true));
136            }
137    
138            protected boolean isDeleteFilesFromSourceStore() {
139                    String[] values = getParameterValues();
140    
141                    return GetterUtil.getBoolean(values[1]);
142            }
143    
144            protected void migrateDL() throws PortalException, SystemException {
145                    int count = DLFileEntryLocalServiceUtil.getFileEntriesCount();
146    
147                    MaintenanceUtil.appendStatus(
148                            "Migrating " + count + " documents and media files");
149    
150                    ActionableDynamicQuery actionableDynamicQuery =
151                            new DLFileEntryActionableDynamicQuery() {
152    
153                            @Override
154                            protected void addCriteria(DynamicQuery dynamicQuery) {
155                                    Property classNameIdProperty = PropertyFactoryUtil.forName(
156                                            "classNameId");
157    
158                                    dynamicQuery.add(classNameIdProperty.eq(0L));
159                            }
160    
161                            @Override
162                            protected void performAction(Object object) throws SystemException {
163                                    DLFileEntry dlFileEntry = (DLFileEntry)object;
164    
165                                    migrateDLFileEntry(
166                                            dlFileEntry.getCompanyId(),
167                                            dlFileEntry.getDataRepositoryId(), dlFileEntry);
168                            }
169    
170                    };
171    
172                    actionableDynamicQuery.performActions();
173    
174                    if (isDeleteFilesFromSourceStore()) {
175                            DLPreviewableProcessor.deleteFiles();
176                    }
177            }
178    
179            protected void migrateDLFileEntry(
180                            long companyId, long repositoryId, DLFileEntry dlFileEntry)
181                    throws SystemException {
182    
183                    String fileName = dlFileEntry.getName();
184    
185                    List<DLFileVersion> dlFileVersions = getDLFileVersions(dlFileEntry);
186    
187                    if (dlFileVersions.isEmpty()) {
188                            String versionNumber = Store.VERSION_DEFAULT;
189    
190                            migrateFile(companyId, repositoryId, fileName, versionNumber);
191    
192                            return;
193                    }
194    
195                    for (DLFileVersion dlFileVersion : dlFileVersions) {
196                            String versionNumber = dlFileVersion.getVersion();
197    
198                            migrateFile(companyId, repositoryId, fileName, versionNumber);
199                    }
200            }
201    
202            protected void migrateFile(
203                    long companyId, long repositoryId, String fileName,
204                    String versionNumber) {
205    
206                    try {
207                            InputStream is = _sourceStore.getFileAsStream(
208                                    companyId, repositoryId, fileName, versionNumber);
209    
210                            if (versionNumber.equals(Store.VERSION_DEFAULT)) {
211                                    _targetStore.addFile(companyId, repositoryId, fileName, is);
212                            }
213                            else {
214                                    _targetStore.updateFile(
215                                            companyId, repositoryId, fileName, versionNumber, is);
216                            }
217    
218                            if (isDeleteFilesFromSourceStore()) {
219                                    _sourceStore.deleteFile(
220                                            companyId, repositoryId, fileName, versionNumber);
221                            }
222                    }
223                    catch (Exception e) {
224                            _log.error("Migration failed for " + fileName, e);
225                    }
226            }
227    
228            protected void migrateImages() throws PortalException, SystemException {
229                    int count = ImageLocalServiceUtil.getImagesCount();
230    
231                    MaintenanceUtil.appendStatus("Migrating " + count + " images");
232    
233                    ActionableDynamicQuery actionableDynamicQuery =
234                            new ImageActionableDynamicQuery() {
235    
236                            @Override
237                            protected void performAction(Object object) {
238                                    Image image = (Image)object;
239    
240                                    String fileName =
241                                            image.getImageId() + StringPool.PERIOD + image.getType();
242    
243                                    migrateFile(0, 0, fileName, Store.VERSION_DEFAULT);
244                            }
245    
246                    };
247    
248                    actionableDynamicQuery.performActions();
249            }
250    
251            protected void migrateMB() throws PortalException, SystemException {
252                    int count = MBMessageLocalServiceUtil.getMBMessagesCount();
253    
254                    MaintenanceUtil.appendStatus(
255                            "Migrating message boards attachments in " + count + " messages");
256    
257                    ActionableDynamicQuery actionableDynamicQuery =
258                            new MBMessageActionableDynamicQuery() {
259    
260                            @Override
261                            protected void performAction(Object object)
262                                    throws PortalException, SystemException {
263    
264                                    MBMessage mbMessage = (MBMessage)object;
265    
266                                    for (FileEntry fileEntry :
267                                                    mbMessage.getAttachmentsFileEntries()) {
268    
269                                            DLFileEntry dlFileEntry = (DLFileEntry)fileEntry.getModel();
270    
271                                            migrateDLFileEntry(
272                                                    mbMessage.getCompanyId(),
273                                                    DLFolderConstants.getDataRepositoryId(
274                                                            dlFileEntry.getRepositoryId(),
275                                                            dlFileEntry.getFolderId()),
276                                                    dlFileEntry);
277                                    }
278                            }
279    
280                    };
281    
282                    actionableDynamicQuery.performActions();
283            }
284    
285            protected void migratePortlets() throws Exception {
286                    migrateImages();
287                    migrateDL();
288                    migrateMB();
289                    migrateWiki();
290            }
291    
292            protected void migrateWiki() throws PortalException, SystemException {
293                    int count = WikiPageLocalServiceUtil.getWikiPagesCount();
294    
295                    MaintenanceUtil.appendStatus(
296                            "Migrating wiki page attachments in " + count + " pages");
297    
298                    ActionableDynamicQuery actionableDynamicQuery =
299                            new WikiPageActionableDynamicQuery() {
300    
301                            @Override
302                            protected void addCriteria(DynamicQuery dynamicQuery) {
303                                    Property property = PropertyFactoryUtil.forName("head");
304    
305                                    dynamicQuery.add(property.eq(true));
306                            }
307    
308                            @Override
309                            protected void performAction(Object object) throws SystemException {
310                                    WikiPage wikiPage = (WikiPage)object;
311    
312                                    for (FileEntry fileEntry :
313                                                    wikiPage.getAttachmentsFileEntries()) {
314    
315                                            DLFileEntry dlFileEntry = (DLFileEntry)fileEntry.getModel();
316    
317                                            migrateDLFileEntry(
318                                                    wikiPage.getCompanyId(),
319                                                    DLFolderConstants.getDataRepositoryId(
320                                                            dlFileEntry.getRepositoryId(),
321                                                            dlFileEntry.getFolderId()),
322                                                    dlFileEntry);
323                                    }
324                            }
325    
326                    };
327    
328                    actionableDynamicQuery.performActions();
329            }
330    
331            private static final String[] _HOOKS = new String[] {
332                    AdvancedFileSystemStore.class.getName(), CMISStore.class.getName(),
333                    DBStore.class.getName(), FileSystemStore.class.getName(),
334                    JCRStore.class.getName(), S3Store.class.getName()
335            };
336    
337            private static Log _log = LogFactoryUtil.getLog(
338                    ConvertDocumentLibrary.class);
339    
340            private Store _sourceStore;
341            private Store _targetStore;
342    
343    }