website logo
➡️ purplepublish.com
Overview
Setup
Editorial
Experience
Growth
Developers
Navigate through spaces
⌘K
Developers Documentation
Catalog-API
Hub Import API
Entitlement services
Purple Growth – Setup
Custom S3 Buckets
Dynamic Resources
Docs powered by archbee 

ACM Sidebar Integration

10min

Integration

Include the following Scripts

HTML
|
<!-- ACM Scripts -->
<script type="text/javascript" src="https://acm.sprylab.com/static/setup/config.js"></script>
<script type="text/javascript" src="https://acm.sprylab.com/static/setup/acmInit.js"></script>


Run the acmService.init function

We Currently only provide a bridge for WordPress Gutenberg (if you have your own CMS, please see below)

JS
|
var acmLoginToken = getACMLoginToken();

var initConfiguration = {
	apiToken: acmLoginToken, // optional
	bridgeSrc: 'https://acm.sprylab.com/static/setup/wpGutenbergBridge.js'
};

window.acmServices.init(initConfiguration);


getACMLoginToken()

This function that can be implemented to authenticate with ACM. It could look like the following:

JS
|
async function getACMLoginToken() {
    var response = await fetch('https://acm.sprylab.com/core/users/login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            username: '',
            password: ''
        })
    });
    return response.json(); // parses JSON response into native JavaScript objects
}

var acmLoginToken = await getACMLoginToken().token;


Add the Sidebar IFrame

min-height: So when not authorized, the Login Page is fully visible

width: The sidebar was built for WordPress mainly, which uses per default 280px

HTML
|
<!-- ACM Sidebar Container -->
<iframe
    id="acmSidebar_iframe"
    src="https://acm.sprylab.com/sidebar"
    style="min-height: 300px !important; width: 280px !important"
/>


Keywords

JS
|
var topics = window.acmBridge.getTopicSelection(); // string[]
var keywords = window.acmBridge.getKeywordSelection(); // string[]
var sidebarSrc = document.getElementById('acmSidebar_iframe').getAttribute('src');

sidebarContainer.setAttribute('src', `${sidebarSrc}?topics=${topics}&keywords=${keywords}`);


Creating your own bridge



When creating your own bridge, you need to modify the initConfiguration

JS
|
var initConfiguration = {
    bridgeSrc: 'URL_TO_BRIDGE_JAVASCRIPT_FILE'
};


The Bridge should look like the following

TypeScript
|
export interface AcmBridge {

    /**
     * initialization method of the bridge. This method is called once the authentication is finished.
     * Any setup tasks which are required for your bridge two work should happen here. This will most likely include
     * subscribing to events fired by your editor (e.g. when the content is changed or saved)
     * The configuration object can be extended as desired if you need to pass additional information
     *
     * @param config the configuration object used an acmServices.init
     */
    init: (config: BridgeConfiguration) => void,

    /**
     *  this method will be called whenever the acm widgets request the content of the editor
     *  Additionally, you should call this method whenever your content has changed.
     *  By default, calling this methods triggers a new analysis in the acm widgets (silent = false).
     *  This may not always be desired because of possible subsequent performance and ux issues.
     *  Therefore it is recommended to call this method with silent set to true for minor or informative changes.
     *  It is also recommended to debounce these calls (e.g. one call per second).
     *
     *  This method is required to send a window message via window.postMessage to the acm widgets iframes.
     *  It is recommended to use acmServices.sendArticleTextChange(article, silent, target) for this purpose.
     *  The first parameter needs to be of type {@link Article} and is required. The silent parameter (see above)
     *  needs to be passed on (can be omitted if false). An optional third parameter can be used to send the message
     *  exclusively to one ACM widget. If this is the case, the contentWindow object of this widget frame
     *  needs to be passed. However this is only an option if you are using multiple ACM widgets.
     *
     * @param {boolean} silent silent content update. Should be used for notify-only updates which are not supposed
     * to trigger immediate updates of the ACM analysis tasks
     * @param {Element} target optional target frame to exclusively process the update to.
     * If this is not set, the message should be sent to all frames which were initially configured
     */
    sendEditorState: (silent: boolean, target?: Element) => void,

    /**
     * This method is called whenever the ACM Widgets have processed the content and altered the editors content.
     * In most cases this will be added markup, e.g. an additional ACM-specific Tag with an Id
     * The article Object passed should be used to update the CMS editor
     *
     * @param {ArticleTextState} article the updated article
     */
    editArticleTextState: (article: ArticleTextState) => void,

    /**
     * This method is called whenever the ACM Widgets have processed the content and updated / added new data.
     * This usually contains additional information to the markup added by {@link editArticleTextState}
     *
     * @param {ArticleDataState} articleData the articleData
     */
    editArticleDataState: (articleData: ArticleDataState) => void

	/**
     * @deprecated Please use redirectToEditorWithURL: (topicIds?: string[], keywords?: string[]) => void
     * This method is called when an ACM Widget urges the CMS to display the content editor, e.g. there is a
     * "write article" button in the Topic Selection widget.
     *
     * @param {string[]} topicIds topicIds to initialize the editor with. Should be passed to the editor in a way
     * so the ACM widgets displayed with the editor can access them (e.g. Query Params). optional
     * @param {string[]} keywords keywords to initialize the editor with. Should be passed to the editor in a way
     * so the ACM widgets displayed with the editor can access them (e.g. Query Params). optional
     */
    redirectToEditor: (topicIds?: string[], keywords?: string[]) => void

    /**
     * This method is called when an ACM Widget urges the CMS to display the content editor, e.g. there is a
     * "write article" button in the Topic Selection widget.
     *
     * @param {string} baseURL the base URL for the link i.e. https://www.wordpress.org/test
     * @param {string[]} topicIds topicIds to initialize the editor with. Should be passed to the editor in a way
     * so the ACM widgets displayed with the editor can access them (e.g. Query Params). optional
     * @param {string[]} keywords keywords to initialize the editor with. Should be passed to the editor in a way
     * so the ACM widgets displayed with the editor can access them (e.g. Query Params). optional
     */

    redirectToEditorWithURL: (baseURL: string, topicIds?: string[], keywords?: string[]) => void      /**
     * This method is called when an ACM Widget urges the CMS to reload the content editor, e. g. editor text was
     * altered by the acm backend and saved to your CMS for example after saving links via the ACM Link Optimizer
     */
    reloadEditor: () => void

    /**
     * This method is called when loading ACM Widgets next to the CMS Editor. Topic Ids which may have been passed
     * to the editor in {@link redirectToEditor} or may be present in the article meta data are passed to the ACM Widgets
     * in this process. This method is used to retrieve the topic ids.
     *
     * @returns {string} comma separated list of topic ids
     */
    getTopicSelection: () => string

    /**
     * This method is called when loading ACM Widgets next to the CMS Editor. Keywords which may have been passed
     * to the editor in {@link redirectToEditor} or may be present in the article meta data are passed to the ACM Widgets
     * in this process. This method is used to retrieve the keywords.
     *
     * @returns {string} comma separated list of topic ids
     */
    getKeywordSelection: () => string
}

Interfaces
interface BridgeConfiguration {

    /**
     * API Token for ACM usage
     */
    apiToken: string,

    /**
     * Array of Target containers in your DOM where the ACM Widgets will be loaded
     * Make sure these Elements are inside the DOM when this configuration is passed to acmServices.init()
     */
    targetDivs: {

        /**
         * name of the ACM component to load as a widget, e.g. 'sidebar'
         */
        component: string,

        /**
         * id of the DOM element which will hold the ACM Widget
         */
        target: string
    }[],

    /**
     * The source location of the bridge script implementation
     */
    bridgeSrc: string
}

interface ArticleTextState {
    article: EditorStateArticle
    silent?: boolean
}

interface ArticleDataState {
    /**
     * link recommendation metadata
     */
    links?: LinkRecommendationResponse[]

    /**
     * Image analysis metadata
     */
    images?: ImageAnalysis[]

    /**
     * Product recommendation metadata
     */
    products?: AmazonProductRecommendationsResponse[]

    /**
     * keyword metadata
     */
    entities?: EntityResponse[]

    /**
     * should the update be silent
     */
    silent?: boolean
}

interface EditorStateArticle {
    /**
     * The id of the article
     */
    article_id: string

    /**
     * Title of the article
     */
    title: string
    
    /**
     * URL of the article in the CMS. optional
     */
    link: string

    /**
     * Raw content of the article (just text, no markup)
     */
    rawContent: string
    
    /**
     * Array of block elements representing the article. optional
     */
    structuredContent: {
        /**
         * original Content of the block
         */
        originalContent: string
    }[]

    /**
     * HTML Content of the article
     */
    htmlContent?: string

    /**
     * The date when it was published
     */
    published?: string
}

interface LinkRecommendationResponse {
    articles: ArticleScoreResponse[]
    badges: {
        most_recent: number
        best_retention_time: number
        most_similar: number
    }
    id: string
    index_paragraph: number
    keyword: string
    profile: LinkRecommendationProfileResponse
    link_parameters: LinkParametersBody
    occurrence_in_paragraph: number
    score: number
    score_articles: number
    score_length: number
    score_selection_to_source: number
    selected_article_id: string
    status: string
    text_selection: string
}

export interface ArticleScoreResponse {
    id: string
    title: string
    overhead: string
    description: string
    author?: string
    ressort?: string
    type?: string
    sub_type?: string
    region?: string
    premium: boolean
    reading_time?: number
    categories: string[]
    keywords: string[]
    link: string
    link_content?: string
    published?: string
    image_url: string
    source: {
        id: string
        title: string
        href: string
    }
    provider: ArticleProvider
    suggested_by: string[]
    content: string
    score: number
    score_retention_time: number
    score_selection_to_target: number
    score_paragraph_to_target: number
    score_source_to_target: number
    score_recency: number
}

enum ArticleProvider {
    CMS = "CMS",
    NEWS = "news",
    WP = "WordPress", // deprecated
    GOOGLE_NEWS = "Google News", // deprecated - wird zu News
    FEEDLY = "FEEDLY", // deprecated - wird zu News
    NEWS_API = "News API", // deprecated - wird zu News
}

interface LinkRecommendationProfileResponse {
    name: string
    article_parameters: ArticleParametersBody
    link_parameters: LinkParametersBody
}

interface ArticleParametersBody {
    avg_links_per_paragraph: number
    preferred_selection_length: number
    selection_length_weight: number
    articles_per_selection: number
    selection_to_source_weight: number
}

interface LinkParametersBody {
    date_range: number
    date_weight: number
    selection_to_target_weight: number
    paragraph_to_target_weight: number
    source_to_target_weight: number
    categories: string[]
    categories_weight: number
}

export interface EntityResponse {
    name: string
    types: string[] | null
    trend_score?: number
    start_char: number
    end_char: number
    wiki_link: string | null
    confidence: number | null
    matched_token: string
    synonyms: Synonym[]
}

export interface Synonym {
    synonym: string
    trend_score: number
}




Updated 03 Mar 2023
Did this page help you?
Yes
No
PREVIOUS
Rest API Documentation
NEXT
Custom S3 Buckets
Docs powered by archbee 
TABLE OF CONTENTS
Integration
Include the following Scripts
Run the acmService.init function
getACMLoginToken()
Add the Sidebar IFrame
Keywords
Creating your own bridge
The Bridge should look like the following
ImprintPrivacy Policy
© Sprylab Technologies GmbH 2022