Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vimeo to supported embed types. #113

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion __extensions_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CommandsExtenderModule } from "./commands-extender";
import { EditorExtenderModule } from "./editor-extender";
import { ItemExtenderModule } from "./item-extender";
import { ThemeModule } from "./theme";
import { EmbedMediaModule } from "./embed-media";

/**
* The entry point of the extensions. Each extension bundle needs to have exactly one export
Expand All @@ -23,7 +24,8 @@ export class SamplesExtension implements Extension {
CommandsExtenderModule,
EditorExtenderModule,
ItemExtenderModule,
ThemeModule
ThemeModule,
EmbedMediaModule
];
}
}
5 changes: 5 additions & 0 deletions embed-media/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Vimeo media parser

As you may know, you can embed media in the rich text editor like YouTube videos, Twitter tweets etc. If you want to enhance your pastes in the editor,
by transforming the pasted value and inserting modified version of it, you can inherit EmbedMediaParser and process the pasted value. In **vimeo-media.parser.ts**, you can check
how is implemented for pasting vimeo url and transforming it into a thumbnail.
10 changes: 10 additions & 0 deletions embed-media/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { VIMEO_EMBED_MEDIA_PROVIDER } from "./vimeo-media.parser";
import { NgModule } from "@angular/core";

@NgModule({
providers: [
VIMEO_EMBED_MEDIA_PROVIDER
]
})

export class EmbedMediaModule { }
46 changes: 46 additions & 0 deletions embed-media/vimeo-media.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { EmbedMediaParser, Media, MediaType } from "progress-sitefinity-adminapp-sdk/app/api/v1";
import { ClassProvider } from "@angular/core";

const VIMEO_URL_REGEX = new RegExp("((?:(?:https?|ftp|file):\/\/|www\.|ftp\.)vimeo.com[/0-9]+)", "i");

/**
* Handles pastes and embed media links for Vimeo.
*/
export class VimeoMediaParser extends EmbedMediaParser {
/**
* Determines if the input text contains Vimeo link.
* @param inputText
*/
canProcess(inputText: string): boolean {
return VIMEO_URL_REGEX.test(inputText);
}

/**
* Parses the input text and retrieves HTML to embed Vimeo video.
* @param inputText
*/
parse(inputText: string): Promise<Media> {
const vimeoUrl = VIMEO_URL_REGEX.exec(inputText)[0];
const endpoint = `https://vimeo.com/api/oembed.json?url=${vimeoUrl}`;

return fetch(endpoint)
.then(r => r.json())
.then(r => {
const media: Media = {
html: r.html,
isValid: true,
height: { value: r.height, hasValue: true },
width: { value: r.width, hasValue: true },
type: MediaType.IFrame
};

return media;
});
}
}

export const VIMEO_EMBED_MEDIA_PROVIDER: ClassProvider = {
multi: true,
provide: EmbedMediaParser,
useClass: VimeoMediaParser
};
38 changes: 34 additions & 4 deletions tests/e2e/app-elements/item-details.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require("jasmine-expect");
import { browser, protractor } from "protractor";
import { browser, protractor, by, element } from "protractor";
import { ItemDetailsMap } from "./item-details.map";
import { BrowserWaitForElement, BrowserVerifyAlert, BrowserWaitForElementHidden, SelectAllAndPasteText } from "../helpers/browser-helpers";
import { BrowserWaitForElement, BrowserVerifyAlert, BrowserWaitForElementHidden, SelectAllAndTypeText } from "../helpers/browser-helpers";
import { EditorPopupMap } from "./editor-popup.map";
import { ItemListMap } from "./item-list.map";
import { EC, TIME_TO_WAIT, TITLE_ERROR, TITLE_VALID_TEXT } from "../helpers/constants";
Expand All @@ -27,6 +27,36 @@ export class ItemDetails {
expect(content).toBe(expectedContent);
}

static async PasteContentInEditor(text: string) {
const addHiddenInput = () => {
const input = document.createElement("input");

input.setAttribute("id", "sf-input-used-to-copy-text-from");

(document.body as any).prepend(input);
};

const removeHiddenInput = () => {
const input = document.getElementById("sf-input-used-to-copy-text-from");

document.body.removeChild(input);
};

await browser.executeScript(addHiddenInput);

const input = element(by.id("sf-input-used-to-copy-text-from"));

await input.sendKeys(text);
await input.sendKeys(protractor.Key.CONTROL, "a");
await input.sendKeys(protractor.Key.CONTROL, "c");

await browser.executeScript(removeHiddenInput);

const editor = ItemDetailsMap.Editor;
await editor.click();
await editor.sendKeys(protractor.Key.CONTROL, "v");
}

static async ChangeEditorContent(newContent: string) {
const viewHTMLButtonTitle = "View HTML";
await BrowserWaitForElement(ItemDetailsMap.ToolbarButtonByTitle(viewHTMLButtonTitle));
Expand All @@ -35,7 +65,7 @@ export class ItemDetails {
await BrowserWaitForElement(ItemDetailsMap.MonacoEditor);
const monacoEditor = ItemDetailsMap.MonacoEditor;
await monacoEditor.click();
await SelectAllAndPasteText(newContent);
await SelectAllAndTypeText(newContent);
await browser.actions().sendKeys(protractor.Key.DELETE).perform();
await browser.actions().sendKeys(protractor.Key.DELETE).perform();
const doneButton = ItemDetailsMap.DoneButton;
Expand Down Expand Up @@ -78,7 +108,7 @@ export class ItemDetails {

const titleInput = ItemDetailsMap.TitleInput;
await titleInput.click();
await SelectAllAndPasteText(TITLE_VALID_TEXT);
await SelectAllAndTypeText(TITLE_VALID_TEXT);

// click html field to loose focus from the title
await ItemDetails.ExpandHtmlField();
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/app-elements/item-details.map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ITEM_CONTENT_BEGINNING } from "../helpers/constants";
const defaultFieldLocator = ".-sf-short-text-default";

export class ItemDetailsMap {
public static Editor = element(by.className("sf-editor"));
public static ExtendedTitleField: ElementFinder = element(by.css(".custom-title-input"));
public static EditorInternalField: ElementFinder = element(by.cssContainingText("div", "Hello everyone"));
public static HtmlFieldExpandButton: ElementFinder = element(by.css("a[title=Expand]"));
Expand Down
25 changes: 23 additions & 2 deletions tests/e2e/app-specs/verify-extensions.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { initAuth } from "../helpers/authentication-manager";
import { ItemList } from "../app-elements/item-list.component";
import {
BrowserNavigate,
SelectAllAndPasteText,
SelectAllAndTypeText,
BrowserVerifyConsoleOutput,
BrowserWaitForElement
} from "../helpers/browser-helpers";
Expand All @@ -26,6 +26,8 @@ import { ItemDetails } from "../app-elements/item-details.component";
import { VideosModal } from "../app-elements/videos-modal.component";
import { Theme } from "../app-elements/theme.component";
import { ItemListMap } from "../app-elements/item-list.map";
import { by } from "protractor";
import { ItemDetailsMap } from "../app-elements/item-details.map";

const ENTITY_MAP = new Map<string, any>()
.set(NEWS_TYPE_NAME, {
Expand Down Expand Up @@ -81,7 +83,7 @@ describe("Verify extensions", () => {
await BrowserNavigate(url);
await ItemList.ClickOnItem(itemToVerify);
await ItemDetails.ExpandHtmlField();
await SelectAllAndPasteText(SAMPLE_TEXT_CONTENT);
await SelectAllAndTypeText(SAMPLE_TEXT_CONTENT);
await ItemDetails.ClickToolbarButtonByTitle("Words count");
await ItemDetails.VerifyHtmlToolbarWordCount(SAMPLE_TEXT_CONTENT);
await ItemDetails.ClickBackButton(true);
Expand Down Expand Up @@ -142,4 +144,23 @@ describe("Verify extensions", () => {
await ItemDetails.FocusHtmlField(); // wait for fields to load
await BrowserVerifyConsoleOutput(itemToVerify);
});

it("recognize Vimeo pastes in RTE", async () => {
await BrowserNavigate(url);
await ItemList.ClickOnCreate();

const videoId = "76979871";
const vimeoUrl = `https://vimeo.com/${videoId}`;

await ItemDetails.PasteContentInEditor(vimeoUrl);

const iframeLocator = ItemDetailsMap.Editor.element(by.css("iframe"));

await BrowserWaitForElement(iframeLocator);

const src = await iframeLocator.getAttribute("src");
const expectedUrl = `https://player.vimeo.com/video/${videoId}`;

expect(src).toContain(expectedUrl);
});
});
2 changes: 1 addition & 1 deletion tests/e2e/helpers/browser-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function BrowserVerifyAlert(expectedAlertText: string): Promise<voi
await alertDialog.accept();
}

export async function SelectAllAndPasteText(text: string): Promise<void> {
export async function SelectAllAndTypeText(text: string): Promise<void> {
await browser.actions().keyDown(protractor.Key.CONTROL).sendKeys("a").perform();
await browser.actions().keyUp(protractor.Key.CONTROL).perform();
await browser.actions().sendKeys(protractor.Key.BACK_SPACE).perform();
Expand Down