Example 1: ReferenceContentMigrator Implementation

public class StoryReferenceMigrator implements ReferencedContentMigrator {

    public static final String LEGACY_PREFIX = "image.";

    private static final String IMG_TAG_NAME = "img";
    private static final String SRC_ATTR_NAME = "src";
    private static final String ALT_ATTR_NAME = "alt";
    private static final String CLASS_ATTR_NAME = "class";
    private static final String BUTTON_TAG_NAME = "button";
    private static final String BUTTON_CSS_CLASS = "enhancement";
    private static final String DATA_ID_ATTR_NAME = "data-id";
    private static final String DATA_REFERENCE_ATTR_NAME = "data-reference";

    @Override
    public Collection<Class<? extends Migrator>> dependencies() {
        Collection<Class<? extends Migrator>> dependencies = new HashSet<>();
        dependencies.add(StoryMigrator.class);

        return dependencies;
    }

    @Override
    public String getContextName() {
        return AcmeMigrationContext.CONTEXT_NAME;
    }

    @Override
    public Class<AcmeMigrationContext> getContextClass() {
        return AcmeMigrationContext.class;
    }

    @Override
    public String getLegacyIdPrefix(MigrationContext context) {
        return StoryMigrator.LEGACY_PREFIX;
    }

    @Override
    public String getReferencedLegacyIdPrefix(MigrationContext context) {
        return LEGACY_PREFIX;
    }

    @Override
    public Class<? extends Recordable> getReferencedContentClass() {
        return Image.class;
    }

    @Override
    public Collection<String> getReferencedLegacyIds(Recordable obj) {

        if (obj instanceof Story && ((Story) obj).getBody() != null) {
            List<String> imageUrls = new ArrayList<>();

            for (Object item : ((Story) obj).getBody()) {
                if (item instanceof String) {
                    Document doc = Jsoup.parse((String) item);
                    Element body = doc.body();

                    for (Element imgElement : body.getElementsByTag(IMG_TAG_NAME)) {
                        if (imgElement.hasAttr(SRC_ATTR_NAME)) {
                            imageUrls.add(imgElement.attr(SRC_ATTR_NAME));
                        }
                    }
                }
            }

            return imageUrls;
        }

        return null;
    }

    @Override
    public void processObject(MigrationContext context, Recordable obj, String imageId, Recordable refObj) {

        Story story = (Story) obj;
        Image image = (Image) refObj;

        if (story.getBody() != null) {
            StringBuilder builder = new StringBuilder();

            for (Object item : story.getBody()) {

                if (item instanceof String) {
                    Document doc = Jsoup.parse((String) item);
                    doc.outputSettings().prettyPrint(false);
                    Element body = doc.body();

                    // Replace Images with image references.
                    for (Element imgElement : body.getElementsByTag(IMG_TAG_NAME)) {
                        String imgSrc = imgElement.attr(SRC_ATTR_NAME);

                        if (imgElement.hasAttr(SRC_ATTR_NAME) && imageId.equals(imgSrc)) {

                            if (imgElement.parent() == null) {
                                // This can happen if same image is referenced twice - it will have been replaced already
                                // and will error on subsequent iterations.
                                continue;
                            }

                            try {
                                // Escape whitespace in URL.
                                String cleanImgSrc = imgSrc.trim().replaceAll(" ", "%20").replaceAll("\r", "").replaceAll("\n", "");
                                Image checkImage = null;

                                // Save binary or just a reference (DEV) to image.
                                StorageItem storageItem;

                                if (((AcmeMigrationContext) context).isDownloadImages()) {
                                    checkImage = Query.from(Image.class)
                                                .where("migration.legacyId = ?", "image." + cleanImgSrc)
                                                .first();

                                    // Don't download and save again if already pulled binary or if only saved URL reference.
                                    if (checkImage == null || (checkImage.getFile() instanceof UrlStorageItem) || (checkImage.getFile() == null)) {
                                        storageItem = StorageItem.Static.create();

                                        String fileName = cleanImgSrc.substring(cleanImgSrc.lastIndexOf("/") + 1);
                                        String hash = StringUtils.hex(StringUtils.md5(fileName));

                                        // Always use a deterministic filename
                                        storageItem.setPath(hash.substring(0, 2) + "/" + hash + "/" + fileName);

                                        String extension = imgSrc.substring(cleanImgSrc.lastIndexOf(".") + 1);
                                        storageItem.setContentType("image/" + extension);

                                        if (!storageItem.isInStorage()) {
                                            URL url = new URL(cleanImgSrc);
                                            URLConnection c = url.openConnection();
                                            c.setConnectTimeout(3000);
                                            c.setReadTimeout(4000);

                                            storageItem.setData(new ByteArrayInputStream(IoUtils.toByteArray(url.openStream())));
                                            storageItem.save();
                                        }

                                        // Add Image field values.
                                        image.setFile(storageItem);
                                    }

                                } else {
                                    storageItem = StorageItem.Static.createUrl(cleanImgSrc);

                                    // Add Image field values.
                                    image.setFile(storageItem);
                                }

                                if (imgElement.hasAttr(ALT_ATTR_NAME)) {
                                    image.setAltText(imgElement.attr(ALT_ATTR_NAME));
                                }

                                // Check for credit
                                Element sibling = imgElement.nextElementSibling();
                                if (sibling != null) {
                                    Element creditElement = sibling.select("em").first();

                                    if (creditElement != null) {
                                        image.setCredit(creditElement.text());
                                    }

                                    sibling.remove();
                                }

                                String fileName = imgSrc.substring(imgSrc.lastIndexOf("/") + 1);
                                String title = fileName.split("\\.")[0];
                                image.setTitle(title);
                                image.as(Site.ObjectModification.class).setGlobal(true);

                                image.saveImmediately();

                                // Replace <img /> tag with Reference as custom RTE tag.
                                Element rte = doc.createElement(ImageRichTextElement.TAG_NAME)
                                        .attr(ImageRichTextElement.TAG_ATTR_NAME_ID, image.getId().toString())
                                        .text(title);

                                imgElement.before("<br/>").after("<br/><br/>");

                                imgElement.replaceWith(rte);

                                if (story.as(Promotable.Data.class).getPromoImage() == null) {
                                    story.as(Promotable.Data.class).setPromoImage(image);
                                }

                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    builder.append(body.html());
                }
            }

            if (builder.length() > 0) {
                ReferentialText refText = new ReferentialText();
                refText.addHtml(builder.toString());

                story.setBody(refText);
            }
        }
    }

    private Element createRteReferenceElement(Document doc, UUID objectId, Map<String, Object> simpleValues) {
        return doc.createElement(BUTTON_TAG_NAME)
            .attr(CLASS_ATTR_NAME, BUTTON_CSS_CLASS)
            .attr(DATA_ID_ATTR_NAME, objectId.toString())
            .attr(DATA_REFERENCE_ATTR_NAME, ObjectUtils.toJson(simpleValues));
    }
}