Example 1: ReferenceContentMigrator Implementation
showLineNumbers
1public class StoryReferenceMigrator implements ReferencedContentMigrator {23public static final String LEGACY_PREFIX = "image.";45private static final String IMG_TAG_NAME = "img";6private static final String SRC_ATTR_NAME = "src";7private static final String ALT_ATTR_NAME = "alt";8private static final String CLASS_ATTR_NAME = "class";9private static final String BUTTON_TAG_NAME = "button";10private static final String BUTTON_CSS_CLASS = "enhancement";11private static final String DATA_ID_ATTR_NAME = "data-id";12private static final String DATA_REFERENCE_ATTR_NAME = "data-reference";1314@Override15public Collection<Class<? extends Migrator>> dependencies() {16Collection<Class<? extends Migrator>> dependencies = new HashSet<>();17dependencies.add(StoryMigrator.class);1819return dependencies;20}2122@Override23public String getContextName() {24return AcmeMigrationContext.CONTEXT_NAME;25}2627@Override28public Class<AcmeMigrationContext> getContextClass() {29return AcmeMigrationContext.class;30}3132@Override33public String getLegacyIdPrefix(MigrationContext context) {34return StoryMigrator.LEGACY_PREFIX;35}3637@Override38public String getReferencedLegacyIdPrefix(MigrationContext context) {39return LEGACY_PREFIX;40}4142@Override43public Class<? extends Recordable> getReferencedContentClass() {44return Image.class;45}4647@Override48public Collection<String> getReferencedLegacyIds(Recordable obj) {4950if (obj instanceof Story && ((Story) obj).getBody() != null) {51List<String> imageUrls = new ArrayList<>();5253for (Object item : ((Story) obj).getBody()) {54if (item instanceof String) {55Document doc = Jsoup.parse((String) item);56Element body = doc.body();5758for (Element imgElement : body.getElementsByTag(IMG_TAG_NAME)) {59if (imgElement.hasAttr(SRC_ATTR_NAME)) {60imageUrls.add(imgElement.attr(SRC_ATTR_NAME));61}62}63}64}6566return imageUrls;67}6869return null;70}7172@Override73public void processObject(MigrationContext context, Recordable obj, String imageId, Recordable refObj) {7475Story story = (Story) obj;76Image image = (Image) refObj;7778if (story.getBody() != null) {79StringBuilder builder = new StringBuilder();8081for (Object item : story.getBody()) {8283if (item instanceof String) {84Document doc = Jsoup.parse((String) item);85doc.outputSettings().prettyPrint(false);86Element body = doc.body();8788// Replace Images with image references.89for (Element imgElement : body.getElementsByTag(IMG_TAG_NAME)) {90String imgSrc = imgElement.attr(SRC_ATTR_NAME);9192if (imgElement.hasAttr(SRC_ATTR_NAME) && imageId.equals(imgSrc)) {9394if (imgElement.parent() == null) {95// This can happen if same image is referenced twice - it will have been replaced already96// and will error on subsequent iterations.97continue;98}99100try {101// Escape whitespace in URL.102String cleanImgSrc = imgSrc.trim().replaceAll(" ", "%20").replaceAll("\r", "").replaceAll("\n", "");103Image checkImage = null;104105// Save binary or just a reference (DEV) to image.106StorageItem storageItem;107108if (((AcmeMigrationContext) context).isDownloadImages()) {109checkImage = Query.from(Image.class)110.where("migration.legacyId = ?", "image." + cleanImgSrc)111.first();112113// Don't download and save again if already pulled binary or if only saved URL reference.114if (checkImage == null || (checkImage.getFile() instanceof UrlStorageItem) || (checkImage.getFile() == null)) {115storageItem = StorageItem.Static.create();116117String fileName = cleanImgSrc.substring(cleanImgSrc.lastIndexOf("/") + 1);118String hash = StringUtils.hex(StringUtils.md5(fileName));119120// Always use a deterministic filename121storageItem.setPath(hash.substring(0, 2) + "/" + hash + "/" + fileName);122123String extension = imgSrc.substring(cleanImgSrc.lastIndexOf(".") + 1);124storageItem.setContentType("image/" + extension);125126if (!storageItem.isInStorage()) {127URL url = new URL(cleanImgSrc);128URLConnection c = url.openConnection();129c.setConnectTimeout(3000);130c.setReadTimeout(4000);131132storageItem.setData(new ByteArrayInputStream(IoUtils.toByteArray(url.openStream())));133storageItem.save();134}135136// Add Image field values.137image.setFile(storageItem);138}139140} else {141storageItem = StorageItem.Static.createUrl(cleanImgSrc);142143// Add Image field values.144image.setFile(storageItem);145}146147if (imgElement.hasAttr(ALT_ATTR_NAME)) {148image.setAltText(imgElement.attr(ALT_ATTR_NAME));149}150151// Check for credit152Element sibling = imgElement.nextElementSibling();153if (sibling != null) {154Element creditElement = sibling.select("em").first();155156if (creditElement != null) {157image.setCredit(creditElement.text());158}159160sibling.remove();161}162163String fileName = imgSrc.substring(imgSrc.lastIndexOf("/") + 1);164String title = fileName.split("\\.")[0];165image.setTitle(title);166image.as(Site.ObjectModification.class).setGlobal(true);167168image.saveImmediately();169170// Replace <img /> tag with Reference as custom RTE tag.171Element rte = doc.createElement(ImageRichTextElement.TAG_NAME)172.attr(ImageRichTextElement.TAG_ATTR_NAME_ID, image.getId().toString())173.text(title);174175imgElement.before("<br/>").after("<br/><br/>");176177imgElement.replaceWith(rte);178179if (story.as(Promotable.Data.class).getPromoImage() == null) {180story.as(Promotable.Data.class).setPromoImage(image);181}182183} catch (Exception e) {184e.printStackTrace();185}186}187}188189builder.append(body.html());190}191}192193if (builder.length() > 0) {194ReferentialText refText = new ReferentialText();195refText.addHtml(builder.toString());196197story.setBody(refText);198}199}200}201202private Element createRteReferenceElement(Document doc, UUID objectId, Map<String, Object> simpleValues) {203return doc.createElement(BUTTON_TAG_NAME)204.attr(CLASS_ATTR_NAME, BUTTON_CSS_CLASS)205.attr(DATA_ID_ATTR_NAME, objectId.toString())206.attr(DATA_REFERENCE_ATTR_NAME, ObjectUtils.toJson(simpleValues));207}208}