Guide to AWS Penetration Testing

Introduction

The popularity of cloud computing is undeniably on the rise and some of the factors contributing to it include scalability, efficiency, flexibility, and reduced IT costs. As the popularity rises, however, there is a worrying Cyber Security Trend that has emerged for organizations and individuals alike.

According to the 2020 Trustwave Global Security Report, the volume of attacks on cloud services has doubled in 2020 as compared to the last year. Cloud environments are now the third most targeted environment for cyber-attacks after corporate and internal networks.

DZone Live: Episode 1

Did you miss episode 1?

Watch it live now on YouTube.

Featured this week:

What is DZone Live?

Catch up with all the latest at DZone! This is the first live-stream show DZone has ever done that will be broadcast across multiple DZone channels on YouTube, Twitch, Twitter, and more. We will highlight DZone contributors to talk about their latest articles and achievements or discuss various trends and topics. We will also be bringing in industry speakers. This live show will also feature the latest DZone news and updates. After this Friday, all future episodes will air live at 11 am every other Thursday.

Get involved with DZone Live! Be a guest!

Do you want to appear in future episodes of DZone Live? Each episode comprises segments that range from 1-5 minutes in length and span about 10 different guests each time.

Flutter Architecture

Introduction

I think there are two concepts that are really fundamental: number one, your architecture for your applications will change over time, right?

Your requirements are going to change. You know, what you use for an MVP or a prototype won’t quite be the same thing that you use for, you know, your app with like 400 to 600,000 lines of code with a team of 20 people. Those things are just naturally going to be different from one another.

Redis Streams in Action (Part 1)

Welcome to this series of blog posts that cover Redis Streams with the help of a practical example. We will use a sample application to make Twitter data available for search and query in real-time. RediSearch and Redis Streams serve as the backbone of this solution that consists of several co-operating components, each of which will be covered in a dedicated blog post.

The code is available in this GitHub repo - https://github.com/abhirockzz/redis-streams-in-action

Visualizing Covid-19 PostgreSQL Data With Arctype

Introduction

As competing cogs in one of the fastest-growing industries within the technology sector, data analysis companies are constantly vying for ways to transform raw SQL queries into impactful visualizations faster and more reliably. Suffice to say, visualization is quite an in-demand skill in today's increasingly complex world of data. While this may sound trivial to outsiders, analysts know this is often anything but straightforward.

Data analysts and software engineers spend time trawling and filtering multiple data sources before getting data ready for the moment of truth—visualizing it. Data visualizations are the culmination of all data crunching work—they're supposed to take long numeric lists and complicated KPIs and present them in an intuitive, easy-to-understand way. That is, if you choose the right visualization tool for your data.

The “Developers (Only) Code” Fallacy

There are plenty of failure possibilities with Scrum. Given that Scrum is a framework with a reasonable yet short “manual,” this effect should not surprise anyone. The Developers Code Fallacy starts with the idea that Developers are rare and expensive and should focus on creating code. Business analysts or customer care agents can talk to customers instead. However, in practice, it has a diminishing effect on a Scrum team’s productivity and creativity. It is a sign for an organization still profoundly stuck in industrial paradigm thinking.

Join me and explore the reasons and the consequences of this Scrum anti-pattern in 110 seconds.

Building A Rich Text Editor (WYSIWYG) From Scratch

In recent years, the field of Content Creation and Representation on Digital platforms has seen a massive disruption. The widespread success of products like Quip, Google Docs and Dropbox Paper has shown how companies are racing to build the best experience for content creators in the enterprise domain and trying to find innovative ways of breaking the traditional moulds of how content is shared and consumed. Taking advantage of the massive outreach of social media platforms, there is a new wave of independent content creators using platforms like Medium to create content and share it with their audience.

As so many people from different professions and backgrounds try to create content on these products, it’s important that these products provide a performant and seamless experience of content creation and have teams of designers and engineers who develop some level of domain expertise over time in this space. With this article, we try to not only lay the foundation of building an editor but also give the readers a glimpse into how little nuggets of functionalities when brought together can create a great user experience for a content creator.

Understanding The Document Structure

Before we dive into building the editor, let’s look at how a document is structured for a Rich Text Editor and what are the different types of data structures involved.

Document Nodes

Document nodes are used to represent the contents of the document. The common types of nodes that a rich-text document could contain are paragraphs, headings, images, videos, code-blocks and pull-quotes. Some of these may contain other nodes as children inside them (e.g. Paragraph nodes contain text nodes inside them). Nodes also hold any properties specific to the object they represent that are needed to render those nodes inside the editor. (e.g. Image nodes contain an image src property, Code-blocks may contain a language property and so on).

There are largely two types of nodes that represent how they should be rendered -

  • Block Nodes (analogous to HTML concept of Block-level elements) that are each rendered on a new line and occupy the available width. Block nodes could contain other block nodes or inline nodes inside them. An observation here is that the top-level nodes of a document would always be block nodes.
  • Inline Nodes (analogous to HTML concept of Inline elements) that start rendering on the same line as the previous node. There are some differences in how inline elements are represented in different editing libraries. SlateJS allows for inline elements to be nodes themselves. DraftJS, another popular Rich Text Editing library, lets you use the concept of Entities to render inline elements. Links and Inline Images are examples of Inline nodes.
  • Void Nodes — SlateJS also allows this third category of nodes that we will use later in this article to render media.

If you want to learn more about these categories, SlateJS’s documentation on Nodes is a good place to start.

Attributes

Similar to HTML’s concept of attributes, attributes in a Rich Text Document are used to represent non-content properties of a node or it’s children. For instance, a text node can have character-style attributes that tell us whether the text is bold/italic/underlined and so on. Although this article represents headings as nodes themselves, another way to represent them could be that nodes have paragraph-styles (paragraph & h1-h6) as attributes on them.

Below image gives an example of how a document’s structure (in JSON) is described at a more granular level using nodes and attributes highlighting some of the elements in the structure to the left.

Some of the things worth calling out here with the structure are:

  • Text nodes are represented as {text: 'text content'}
  • Properties of the nodes are stored directly on the node (e.g. url for links and caption for images)
  • SlateJS-specific representation of text attributes breaks the text nodes to be their own nodes if the character style changes. Hence, the text ‘Duis aute irure dolor’ is a text node of it’s own with bold: true set on it. Same is the case with the italic, underline and code style text in this document.

Locations And Selection

When building a rich text editor, it is crucial to have an understanding of how the most granular part of a document (say a character) can be represented with some sort of coordinates. This helps us navigate the document structure at runtime to understand where in the document hierarchy we are. Most importantly, location objects give us a way to represent user selection which is quite extensively used to tailor the user experience of the editor in real time. We will use selection to build our toolbar later in this article. Examples of these could be:

  • Is the user’s cursor currently inside a link, maybe we should show them a menu to edit/remove the link?
  • Has the user selected an image? Maybe we give them a menu to resize the image.
  • If the user selects certain text and hits the DELETE button, we determine what user’s selected text was and remove that from the document.

SlateJS’s document on Location explains these data structures extensively but we go through them here quickly as we use these terms at different instances in the article and show an example in the diagram that follows.

  • Path
    Represented by an array of numbers, a path is the way to get to a node in the document. For instance, a path [2,3] represents the 3rd child node of the 2nd node in the document.
  • Point
    More granular location of content represented by path + offset. For instance, a point of {path: [2,3], offset: 14} represents the 14th character of the 3rd child node inside the 2nd node of the document.
  • Range
    A pair of points (called anchor and focus) that represent a range of text inside the document. This concept comes from Web’s Selection API where anchor is where user’s selection began and focus is where it ended. A collapsed range/selection denotes where anchor and focus points are the same (think of a blinking cursor in a text input for instance).

As an example let’s say that the user’s selection in our above document example is ipsum:

The user’s selection can be represented as:

{
  anchor: {path: [2,0], offset: 5}, /0th text node inside the paragraph node which itself is index 2 in the document/
  focus: {path: [2,0], offset: 11}, // space + 'ipsum'
}`

Setting Up The Editor

In this section, we are going to set up the application and get a basic rich-text editor going with SlateJS. The boilerplate application would be create-react-app with SlateJS dependencies added to it. We are building the UI of the application using components from react-bootstrap. Let’s get started!

Create a folder called wysiwyg-editor and run the below command from inside the directory to set up the react app. We then run a yarn start command that should spin up the local web server (port defaulting to 3000) and show you a React welcome screen.

npx create-react-app .
yarn start

We then move on to add the SlateJS dependencies to the application.

yarn add slate slate-react

slate is SlateJS's core package and slate-react includes the set of React components we will use to render Slate editors. SlateJS exposes some more packages organized by functionality one might consider adding to their editor.

We first create a utils folder that holds any utility modules we create in this application. We start with creating an ExampleDocument.js that returns a basic document structure that contains a paragraph with some text. This module looks like below:

const ExampleDocument = [
  {
    type: "paragraph",
    children: [
      { text: "Hello World! This is my paragraph inside a sample document." },
    ],
  },
];

export default ExampleDocument;

We now add a folder called components that will hold all our React components and do the following:

  • Add our first React component Editor.js to it. It only returns a div for now.
  • Update the App.js component to hold the document in its state which is initialized to our ExampleDocument above.
  • Render the Editor inside the app and pass the document state and an onChange handler down to the Editor so our document state is updated as the user updates it.
  • We use React bootstrap’s Nav components to add a navigation bar to the application as well.

App.js component now looks like below:

import Editor from './components/Editor';

function App() {
  const [document, updateDocument] = useState(ExampleDocument);

  return (
    <>
      <Navbar bg="dark" variant="dark">
        <Navbar.Brand href="#">
          <img
            alt=""
            src="/app-icon.png"
            width="30"
            height="30"
            className="d-inline-block align-top"
          />{" "}
          WYSIWYG Editor
        </Navbar.Brand>
      </Navbar>
      <div className="App">
        <Editor document={document} onChange={updateDocument} />
      </div>
    </>
  );

Inside the Editor component, we then instantiate the SlateJS editor and hold it inside a useMemo so that the object doesn’t change in between re-renders.

// dependencies imported as below.
import { withReact } from "slate-react";
import { createEditor } from "slate";

const editor = useMemo(() => withReact(createEditor()), []);

createEditor gives us the SlateJS editor instance which we use extensively through the application to access selections, run data transformations and so on. withReact is a SlateJS plugin that adds React and DOM behaviors to the editor object. SlateJS Plugins are Javascript functions that receive the editor object and attach some configuration to it. This allows web developers to add configurations to their SlateJS editor instance in a composable way.

We now import and render <Slate /> and <Editable /> components from SlateJS with the document prop we get from App.js. Slate exposes a bunch of React contexts we use to access in the application code. Editable is the component that renders the document hierarchy for editing. Overall, the Editor.js module at this stage looks like below:

import { Editable, Slate, withReact } from "slate-react";

import { createEditor } from "slate";
import { useMemo } from "react";

export default function Editor({ document, onChange }) {
  const editor = useMemo(() => withReact(createEditor()), []);
  return (
    <Slate editor={editor} value={document} onChange={onChange}>
      <Editable />
    </Slate>
  );
}

At this point, we have necessary React components added and the editor populated with an example document. Our Editor should be now set up allowing us to type in and change the content in real time — as in the screencast below.

Character Styles

Similar to renderElement, SlateJS gives out a function prop called renderLeaf that can be used to customize rendering of the text nodes (Leaf referring to text nodes which are the leaves/lowest level nodes of the document tree). Following the example of renderElement, we write an implementation for renderLeaf.

export default function useEditorConfig(editor) {
  return { renderElement, renderLeaf };
}

// ...
function renderLeaf({ attributes, children, leaf }) {
  let el = <>{children}</>;

  if (leaf.bold) {
    el = <strong>{el}</strong>;
  }

  if (leaf.code) {
    el = <code>{el}</code>;
  }

  if (leaf.italic) {
    el = <em>{el}</em>;
  }

  if (leaf.underline) {
    el = <u>{el}</u>;
  }

  return <span {...attributes}>{el}</span>;
}

An important observation of the above implementation is that it allows us to respect HTML semantics for character styles. Since renderLeaf gives us access to the text node leaf itself, we can customize the function to implement a more customized rendering. For instance, you might have a way to let users choose a highlightColor for text and check that leaf property here to attach the respective styles.

We now update the Editor component to use the above, the ExampleDocument to have a few text nodes in the paragraph with combinations of these styles and verify that they are rendered as expected in the Editor with the semantic tags we used.

# src/components/Editor.js

const { renderElement, renderLeaf } = useEditorConfig(editor);

return (
    ...
    <Editable renderElement={renderElement} renderLeaf={renderLeaf} />
);
# src/utils/ExampleDocument.js

{
    type: "paragraph",
    children: [
      { text: "Hello World! This is my paragraph inside a sample document." },
      { text: "Bold text.", bold: true, code: true },
      { text: "Italic text.", italic: true },
      { text: "Bold and underlined text.", bold: true, underline: true },
      { text: "variableFoo", code: true },
    ],
  },

Adding A Toolbar

Let’s begin by adding a new component Toolbar.js to which we add a few buttons for character styles and a dropdown for paragraph styles and we wire these up later in the section.

const PARAGRAPH_STYLES = ["h1", "h2", "h3", "h4", "paragraph", "multiple"];
const CHARACTER_STYLES = ["bold", "italic", "underline", "code"];

export default function Toolbar({ selection, previousSelection }) {
  return (
    <div className="toolbar">
      {/* Dropdown for paragraph styles */}
      <DropdownButton
        className={"block-style-dropdown"}
        disabled={false}
        id="block-style"
        title={getLabelForBlockStyle("paragraph")}
      >
        {PARAGRAPH_STYLES.map((blockType) => (
          <Dropdown.Item eventKey={blockType} key={blockType}>
            {getLabelForBlockStyle(blockType)}
          </Dropdown.Item>
        ))}
      </DropdownButton>
      {/* Buttons for character styles */}
      {CHARACTER_STYLES.map((style) => (
        <ToolBarButton
          key={style}
          icon={<i className={`bi ${getIconForButton(style)}`} />}
          isActive={false}
        />
      ))}
    </div>
  );
}

function ToolBarButton(props) {
  const { icon, isActive, ...otherProps } = props;
  return (
    <Button
      variant="outline-primary"
      className="toolbar-btn"
      active={isActive}
      {...otherProps}
    >
      {icon}
    </Button>
  );
}

We abstract away the buttons to the ToolbarButton component that is a wrapper around the React Bootstrap Button component. We then render the toolbar above the Editable inside Editor component and verify that the toolbar shows up in the application.

Here are the three key functionalities we need the toolbar to support:

  1. When the user’s cursor is in a certain spot in the document and they click one of the character style buttons, we need to toggle the style for the text they may type next.
  2. When the user selects a range of text and click one of the character style buttons, we need to toggle the style for that specific section.
  3. When the user selects a range of text, we want to update the paragraph-style dropdown to reflect the paragraph-type of the selection. If they do select a different value from the selection, we want to update the paragraph style of the entire selection to be what they selected.

Let’s look at how these functionalities work on the Editor before we start implementing them.

Adding A Link Button To The Toolbar

Let’s add a Link Button to the toolbar that enables the user to do the following:

  • Selecting some text and clicking on the button converts that text into a link
  • Having a blinking cursor (collapsed selection) and clicking the button inserts a new link there
  • If the user’s selection is inside a link, clicking on the button should toggle the link — meaning convert the link back to text.

To build these functionalities, we need a way in the toolbar to know if the user’s selection is inside a link node. We add a util function that traverses the levels in upward direction from the user’s selection to find a link node if there is one, using Editor.above helper function from SlateJS.

# src/utils/EditorUtils.js

export function isLinkNodeAtSelection(editor, selection) {
  if (selection == null) {
    return false;
  }

  return (
    Editor.above(editor, {
      at: selection,
      match: (n) => n.type === "link",
    }) != null
  );
}

Now, let’s add a button to the toolbar that is in active state if the user's selection is inside a link node.

# src/components/Toolbar.js

return (
    <div className="toolbar">
      ...
      {/* Link Button */}
      <ToolBarButton
        isActive={isLinkNodeAtSelection(editor, editor.selection)}
        label={<i className={`bi ${getIconForButton("link")}`} />}
      />
    </div>
  );

If we had to do this by ourselves, we’d have to figure out the range of selection and create three new nodes (text, link, text) that replace the original text node. SlateJS has a helper function called Transforms.wrapNodes that does exactly this — wrap nodes at a location into a new container node. We also have a helper available for the reverse of this process — Transforms.unwrapNodes which we use to remove links from selected text and merge that text back into the text nodes around it. With that, toggleLinkAtSelection has the below implementation to insert a new link at an expanded selection.

# src/utils/EditorUtils.js

export function toggleLinkAtSelection(editor) {
  if (!isLinkNodeAtSelection(editor, editor.selection)) {
    const isSelectionCollapsed =
      Range.isCollapsed(editor.selection);
    if (isSelectionCollapsed) {
      Transforms.insertNodes(
        editor,
        {
          type: "link",
          url: '#',
          children: [{ text: 'link' }],
        },
        { at: editor.selection }
      );
    } else {
      Transforms.wrapNodes(
        editor,
        { type: "link", url: '#', children: [{ text: '' }] },
        { split: true, at: editor.selection }
      );
    }
  } else {
    Transforms.unwrapNodes(editor, {
      match: (n) => Element.isElement(n) && n.type === "link",
    });
  }
}

If the selection is collapsed, we insert a new node there with Transform.insertNodes that inserts the node at the given location in the document. We wire this function up with the toolbar button and should now have a way to add/remove links from the document with the help of the link button.

# src/components/Toolbar.js
      <ToolBarButton
        ...
        isActive={isLinkNodeAtSelection(editor, editor.selection)}       
        onMouseDown={() => toggleLinkAtSelection(editor)}
      />

If the text ’ABCDE’ was the first text node of the first paragraph in the document, our point values would be —

cursorPoint = { path: [0,0], offset: 5}
startPointOfLastCharacter = { path: [0,0], offset: 4}

If the last character was a space, we know where it started — startPointOfLastCharacter.Let’s move to step-2 where we move backwards character-by-character until either we find another space or the start of the text node itself.

...

  if (lastCharacter !== " ") {
    return;
  }

  let end = startPointOfLastCharacter;
  start = Editor.before(editor, end, {
    unit: "character",
  });

  const startOfTextNode = Editor.point(editor, currentNodePath, {
    edge: "start",
  });

  while (
    Editor.string(editor, Editor.range(editor, start, end)) !== " " &&
    !Point.isBefore(start, startOfTextNode)
  ) {
    end = start;
    start = Editor.before(editor, end, { unit: "character" });
  }

  const lastWordRange = Editor.range(editor, end, startPointOfLastCharacter);
  const lastWord = Editor.string(editor, lastWordRange);

Here is a diagram that shows where these different points point to once we find the last word entered to be ABCDE.

Note that start and end are the points before and after the space there. Similarly, startPointOfLastCharacter and cursorPoint are the points before and after the space user just inserted. Hence [end,startPointOfLastCharacter] gives us the last word inserted.

We log the value of lastWord to the console and verify the values as we type.

Now let’s focus on caption-editing. The way we want this to be a seamless experience for the user is that when they click on the caption, we show a text input where they can edit the caption. If they click outside the input or hit the RETURN key, we treat that as a confirmation to apply the caption. We then update the caption on the image node and switch the caption back to read mode. Let’s see it in action so we have an idea of what we’re building.

Let’s update our Image component to have a state for caption’s read-edit modes. We update the local caption state as the user updates it and when they click out (onBlur) or hit RETURN (onKeyDown), we apply the caption to the node and switch to read mode again.

const Image = ({ attributes, children, element }) => {
  const [isEditingCaption, setEditingCaption] = useState(false);
  const [caption, setCaption] = useState(element.caption);
  ...

  const applyCaptionChange = useCallback(
    (captionInput) => {
      const imageNodeEntry = Editor.above(editor, {
        match: (n) => n.type === "image",
      });
      if (imageNodeEntry == null) {
        return;
      }

      if (captionInput != null) {
        setCaption(captionInput);
      }

      Transforms.setNodes(
        editor,
        { caption: captionInput },
        { at: imageNodeEntry[1] }
      );
    },
    [editor, setCaption]
  );

  const onCaptionChange = useCallback(
    (event) => {
      setCaption(event.target.value);
    },
    [editor.selection, setCaption]
  );

  const onKeyDown = useCallback(
    (event) => {
      if (!isHotkey("enter", event)) {
        return;
      }

      applyCaptionChange(event.target.value);
      setEditingCaption(false);
    },
    [applyCaptionChange, setEditingCaption]
  );

  const onToggleCaptionEditMode = useCallback(
    (event) => {
      const wasEditing = isEditingCaption;
      setEditingCaption(!isEditingCaption);
      wasEditing && applyCaptionChange(caption);
    },
    [editor.selection, isEditingCaption, applyCaptionChange, caption]
  );

  return (
        ...
        {isEditingCaption ? (
          <Form.Control
            autoFocus={true}
            className={"image-caption-input"}
            size="sm"
            type="text"
            defaultValue={element.caption}
            onKeyDown={onKeyDown}
            onChange={onCaptionChange}
            onBlur={onToggleCaptionEditMode}
          />
        ) : (
          <div
            className={"image-caption-read-mode"}
            onClick={onToggleCaptionEditMode}
          >
            {caption}
          </div>
        )}
      </div>
      ...

With that, the caption editing functionality is complete. We now move to adding a way for users to upload images to the editor. Let’s add a toolbar button that lets users select and upload an image.

# src/components/Toolbar.js

const onImageSelected = useImageUploadHandler(editor, previousSelection);

return (
    <div className="toolbar">
    ....
   <ToolBarButton
        isActive={false}
        as={"label"}
        htmlFor="image-upload"
        label={
          <>
            <i className={`bi ${getIconForButton("image")}`} />
            <input
              type="file"
              id="image-upload"
              className="image-upload-input"
              accept="image/png, image/jpeg"
              onChange={onImageSelected}
            />
          </>
        }
      />
    </div>

As we work with image uploads, the code could grow quite a bit so we move the image-upload handling to a hook useImageUploadHandler that gives out a callback attached to the file-input element. We’ll discuss shortly about why it needs the previousSelection state.

Before we implement useImageUploadHandler, we’ll set up the server to be able to upload an image to. We setup an Express server and install two other packages — cors and multer that handle file uploads for us.

yarn add express cors multer

We then add a src/server.js script that configures the Express server with cors and multer and exposes an endpoint /upload which we will upload the image to.

# src/server.js

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "./public/photos/");
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  },
});

var upload = multer({ storage: storage }).single("photo");

app.post("/upload", function (req, res) {
  upload(req, res, function (err) {
    if (err instanceof multer.MulterError) {
      return res.status(500).json(err);
    } else if (err) {
      return res.status(500).json(err);
    }
    return res.status(200).send(req.file);
  });
});

app.use(cors());
app.listen(port, () => console.log(`Listening on port ${port}`));

Now that we have the server setup, we can focus on handling the image upload. When the user uploads an image, it could be a few seconds before the image gets uploaded and we have a URL for it. However, we do what to give the user immediate feedback that the image upload is in progress so that they know the image is being inserted in the editor. Here are the steps we implement to make this behavior work -

  1. Once the user selects an image, we insert an image node at the user’s cursor position with a flag isUploading set on it so we can show the user a loading state.
  2. We send the request to the server to upload the image.
  3. Once the request is complete and we have an image URL, we set that on the image and remove the loading state.

Let’s begin with the first step where we insert the image node. Now, the tricky part here is we run into the same issue with selection as with the link button in the toolbar. As soon as the user clicks on the Image button in the toolbar, the editor loses focus and the selection becomes null. If we try to insert an image, we don’t know where the user's cursor was. Tracking previousSelection gives us that location and we use that to insert the node.

# src/hooks/useImageUploadHandler.js
import { v4 as uuidv4 } from "uuid";

export default function useImageUploadHandler(editor, previousSelection) {
  return useCallback(
    (event) => {
      event.preventDefault();
      const files = event.target.files;
      if (files.length === 0) {
        return;
      }
      const file = files[0];
      const fileName = file.name;
      const formData = new FormData();
      formData.append("photo", file);

      const id = uuidv4();

      Transforms.insertNodes(
        editor,
        {
          id,
          type: "image",
          caption: fileName,
          url: null,
          isUploading: true,
          children: [{ text: "" }],
        },
        { at: previousSelection, select: true }
      );
    },
    [editor, previousSelection]
  );
}

As we insert the new image node, we also assign it an identifier id using the uuid package. We’ll discuss in Step (3)’s implementation why we need that. We now update the image component to use the isUploading flag to show a loading state.

{!element.isUploading && element.url != null ? (
   <img src={element.url} alt={caption} className={"image"} />
) : (
   <div className={"image-upload-placeholder"}>
        <Spinner animation="border" variant="dark" />
   </div>
)}

That completes the implementation of step 1. Let’s verify that we are able to select an image to upload, see the image node getting inserted with a loading indicator where it was inserted in the document.

Moving to Step (2), we will use axois library to send a request to the server.

export default function useImageUploadHandler(editor, previousSelection) {
  return useCallback((event) => {
    ....
    Transforms.insertNodes(
     …
     {at: previousSelection, select: true}
    );

    axios
      .post("/upload", formData, {
        headers: {
          "content-type": "multipart/form-data",
        },
      })
      .then((response) => {
           // update the image node.
       })
      .catch((error) => {
        // Fire another Transform.setNodes to set an upload failed state on the image
      });
  }, [...]);
}

We verify that the image upload works and the image does show up in the public/photos folder of the app. Now that the image upload is complete, we move to Step (3) where we want to set the URL on the image in the resolve() function of the axios promise. We could update the image with Transforms.setNodes but we have a problem — we do not have the path to the newly inserted image node. Let’s see what our options are to get to that image —

  • Can’t we use editor.selection as the selection must be on the newly inserted image node? We cannot guarantee this since while the image was uploading, the user might have clicked somewhere else and the selection might have changed.
  • How about using previousSelection which we used to insert the image node in the first place? For the same reason we can’t use editor.selection, we can’t use previousSelection since it may have changed too.
  • SlateJS has a History module that tracks all the changes happening to the document. We could use this module to search the history and find the last inserted image node. This also isn’t completely reliable if it took longer for the image to upload and the user inserted more images in different parts of the document before the first upload completed.
  • Currently, Transform.insertNodes’s API doesn’t return any information about the inserted nodes. If it could return the paths to the inserted nodes, we could use that to find the precise image node we should update.

Since none of the above approaches work, we apply an id to the inserted image node (in Step (1)) and use the same id again to locate it when the image upload is complete. With that, our code for Step (3) looks like below —

axios
        .post("/upload", formData, {
          headers: {
            "content-type": "multipart/form-data",
          },
        })
        .then((response) => {
          const newImageEntry = Editor.nodes(editor, {
            match: (n) => n.id === id,
          });

          if (newImageEntry == null) {
            return;
          }

          Transforms.setNodes(
            editor,
            { isUploading: false, url: `/photos/${fileName}` },
            { at: newImageEntry[1] }
          );
        })
        .catch((error) => {
          // Fire another Transform.setNodes to set an upload failure state
          // on the image.        
        });

With the implementation of all three steps complete, we are ready to test the image upload end to end.

With that, we’ve wrapped up Images for our editor. Currently, we show a loading state of the same size irrespective of the image. This could be a jarring experience for the user if the loading state is replaced by a drastically smaller or bigger image when the upload completes. A good follow up to the upload experience is getting the image dimensions before the upload and showing a placeholder of that size so that transition is seamless. The hook we add above could be extended to support other media types like video or documents and render those types of nodes as well.

Conclusion

In this article, we have built a WYSIWYG Editor that has a basic set of functionalities and some micro user-experiences like link detection, in-place link editing and image caption editing that helped us go deeper with SlateJS and concepts of Rich Text Editing in general. If this problem space surrounding Rich Text Editing or Word Processing interests you, some of the cool problems to go after could be:

  • Collaboration
  • A richer text editing experience that supports text alignments, inline images, copy-paste, changing font and text colors etc.
  • Importing from popular formats like Word documents and Markdown.

If you want to learn more SlateJS, here are some links that might be helpful.

  • SlateJS Examples
    A lot of examples that go beyond the basics and build functionalities that are usually found in Editors like Search & Highlight, Markdown Preview and Mentions.
  • API Docs
    Reference to a lot of helper functions exposed by SlateJS that one might want to keep handy when trying to perform complex queries/transformations on SlateJS objects.

Lastly, SlateJS’s Slack Channel is a very active community of web developers building Rich Text Editing applications using SlateJS and a great place to learn more about the library and get help if needed.

Migrating My Heroku-Based SaaS to Heroku Postgres

Over the course of my 30+ year career in Information Technology, I have encountered my fair share of proof-of-concept or prototype applications which have found their way into a productional state. This has always been a direct conflict with the original intent of the work that was created.

While this should be considered some form of a compliment for a job well-done, more often than not there are decisions made in those prototyping exercises which are not labeled as production-support friendly. To some degree, this very same scenario exists with the Heroku-based SaaS fitness solution I originally created for my sister-in-law.

9 Best YouTube Video Gallery Plugins for WordPress

Do you want to add a YouTube video gallery on your WordPress website?

You can add YouTube videos to WordPress blog posts using the built-in Embed block. However, you can’t easily arrange these videos in a nice gallery layout.

In this article, we will share the best YouTube video gallery plugins for WordPress so you can show your videos in an organized and eye-catching way.

Best YouTube video gallery plugins for WordPress

1. Smash Balloon YouTube Feed Pro

The Smash Balloon YouTube gallery plugin

Smash Balloon YouTube Feed Pro is the best YouTube gallery plugin on the market.

This plugin allows you to hand-pick specific videos from your account, or automatically show all the latest videos from your YouTube channel. You can even combine multiple YouTube channels into a single feed.

A YouTube gallery, created using Smash Balloon

Smash Balloon has a ready-made gallery layout so you can organize your videos with the click of a button. After creating a gallery, you can control exactly how the videos look by choosing whether to show the YouTube description, title, comments, the total number of likes, and much more.

You can even encourage visitors to join your YouTube channel, by adding a ‘Subscribe’ button to the gallery.

By default, the gallery layout also has a ‘Load More’ button so visitors can scroll through your entire YouTube channel without leaving your website.

Customizing the YouTube gallery 'Load More' button

When a visitor clicks on a video, Smash Balloon will start playing it in an embedded video player. In this way, you can get more YouTube views and engagement without driving visitors away from your website.

By default, the player shows related videos when the playback finishes, or the visitor pauses the video. Smash Balloon lets you replace these suggested videos with a custom call to action, so you can promote a specific post, page, or even the most popular WooCommerce products from your online store.

You can also use Smash Balloon to embed YouTube playlists, favorites, and livestreams on your WordPress website.

The best part about Smash Balloon is that it doesn’t slow down your site. The plugin’s built-in YouTube caching feature will make sure your site always loads fast, which is great for SEO.

The other nice thing about Smash Balloon is that you can combine social content from Instagram, Facebook, Twitter, and YouTube into a single social wall. If you’re serious about growing your online presence, then we highly recommend the Smash Balloon All Access Bundle, which gives you access to the social wall feature.

A social wall, created using Smash Balloon

Note: If you’re just getting started or have a limited budget, then there’s also a free version of the YouTube feed plugin. Although this plugin is missing the more advanced features, it still has everything you need to embed YouTube videos on your WordPress website.

Pricing: Smash Balloon YouTube Feed Pro licenses start at $49 per year. If you want to embed Facebook, Twitter, Instagram, and YouTube content on your WordPress website, then you can grab the All Access Bundle for $299 per year.

2. Envira Gallery

The Envira Gallery YouTube plugin

Envira Gallery is generally known as the best WordPress photo gallery plugin.

However, there’s also an Envira Gallery Videos Addon that allows you to create a YouTube gallery. Simply enter the URL for each YouTube video and then specify the thumbnail that you want to show in the gallery. The Videos Addon will then fetch the video automatically.

After adding all your videos, Envira Gallery lets you fine-tune the playback experience. You can make the video autoplay, open it fullscreen by default, hide the playback controls, and much more.

Envira Gallery can also embed entire YouTube playlists, although you’ll need to get a YouTube API from the Google Cloud Console and then add it to the plugin’s settings first.

As well as YouTube, Envira Gallery supports other popular video hosting services such as Vimeo, Wistia, Twitch, VideoPress, and Dailymotion.

You can also embed Facebook videos in WordPress.

One major drawback is that Envira Gallery won’t automatically fetch new YouTube videos from your channel. If you want to update the gallery, then you’ll need to add each new video manually using its URL.

Pricing: To get Envira with the Video Gallery addon, you’ll need to either buy a Pro license ($89 per year) or a Lifetime license ($209).

3. YouTube Showcase

The YouTube Showcase gallery plugin

With the YouTube Showcase plugin, you can add a YouTube video gallery or grid to your WordPress website.

After activating the plugin, you can add any YouTube video to your site using the 11-digit alphanumeric video key shown at the end of the video’s URL.

To help visitors find what they’re looking for, YouTube Showcase lets you add categories and tags to your videos. These work in exactly the same way as the built-in WordPress categories and tags, so they’re very easy to use.

Once you’ve done that, you just need to add the plugin’s shortcode to any page, post, or widget-ready area. YouTube Showcase will then show all your videos in a nice gallery layout.

YouTube Showcase automatically adds a row of video thumbnails below the gallery, so visitors can easily move between the different videos. For visitors who are using a smartphone or tablet, YouTube Showcase replaces these thumbnails with more mobile-friendly ‘Prev’ and ‘Next’ icons.

The plugin uses pagination by default, so your gallery should load quickly no matter how many videos you add.

The default gallery layout should be a good fit for most websites. However, if you want to make changes then the plugin has a straightforward settings page.

While it isn’t as customizable as some of the other gallery plugins, YouTube Showcase does have a section where more advanced users can add custom CSS and custom JavaScript.

If you don’t like the gallery layout, then YouTube Showcase also has a video grid shortcode. This shows all your YouTube videos in a 4-column grid layout.

Pricing: You can download YouTube Showcase for free from the official WordPress repository.

4. YourChannel

The YourChannel WordPress gallery plugin for YouTube

YourChannel is one of the easiest ways to create a YouTube gallery. It allows visitors to look through your entire YouTube channel feed and watch any video without leaving your website.

YourChannel also comes with a built-in WordPress caching system, to help keep your website running smoothly.

To set up YourChannel, simply enter your YouTube username or channel ID. After that, the plugin will automatically fetch banner images, video thumbnails, your subscriber count, and your total number of views.

The plugin’s settings are straightforward and user-friendly, so you can create a beautiful-looking gallery simply by checking a few boxes and typing some numbers.

You can change how many videos YourChannel shows when the gallery page first loads, and set a maximum number of videos that visitors can scroll through. This is particularly useful for creating a mobile-friendly video gallery.

YourChannel also helps you stay GDPR compliant by showing an ‘Allow Cookies’ message when a visitor first clicks the ‘Play’ button. This message includes a link to your privacy policy.

When you’re happy with how the gallery looks, you can add it to any page, post, or widget-ready area using a shortcode provided by the plugin.

Pricing: Simply visit the WordPress repository and download the YourChannel plugin for free.

5. Photo Gallery by 10Web

The Photo Gallery for 10Web plugin for WordPress

According to our marketing statistics research, 70% of people visit YouTube on a smartphone or tablet. If you run an online store using a plugin such as WooCommerce, then you’ll be interested to learn that 90% of consumers watch videos on a mobile device.

With that in mind, you’ll want to make sure your YouTube gallery looks just as good on mobile, as it does on desktop. With Photo Gallery by 10Web, you can create a mobile responsive image and video gallery within minutes.

Despite the name, Photo Gallery supports all the best video hosting sites including Vimeo, Dailymotion, and YouTube. Simply copy the video’s URL, and Photo Gallery will add that video to your website.

After that, you can fine-tune how the gallery looks by choosing different layouts, switching between dark and light themes, adding pagination, and more. You’ll also have complete control over the content that appears in your gallery, including adding or removing the video title, description, tag box, and more.

Visitors can watch each video directly on your website, using the Photo Gallery lightbox popup. The lightbox comes with built-in social share buttons, which make it easy for visitors to share your videos on Facebook and Twitter. This can help you create a buzz on social media, get more engagement, and grow your social media following.

Pricing: You can download Photo Gallery for free from the official WordPress repository. If you need more advanced features, then there’s also a premium version of Photo Gallery.

6. Embed Plus for YouTube

The EmbedPlus gallery plugin for YouTube

Embed Plus for YouTube allows you to create a YouTube gallery using videos, playlists, shorts, and livestreams.

This plugin has a few unusual features, such as ‘Modest Branding’ which reduces the amount of YouTube branding in your video gallery.

Another unique feature is ‘facade mode,’ which aims to make your WordPress website faster by loading a lighter version of the video player initially, and then downloading the rest of the player when the visitor clicks a video.

To make your pages load even faster, Embed Plus can defer JavaScript while serving minified CSS and JavaScript.

Embed Plus can even convert non-HTTPs videos to HTTPS. Since Google uses HTTPS/SSL as a ranking factor in its search results, this can improve your search engine rankings.

For more on this topic, please see our complete guide on how to move WordPress from HTTP to HTTPS.

To keep people on your website for longer, Embed Plus can automatically play the next video in the gallery, which is particularly useful if you’re adding playlists to your WordPress blog.

This plugin has in-depth and detailed settings that cover several tabs. This gives you complete control over how the YouTube gallery looks and acts, but it can be confusing when you first activate the plugin.

Thankfully, Embed Plus has a built-in setup wizard that asks questions about the kind of gallery you want to create and then changes the default settings based on your answers.

Pricing: You can download Embed Plus YouTube for free from the official WordPress repository. If you need more advanced features then there’s also a Pro YouTube plugin.

7. Automatic YouTube Gallery

The Automatic YouTube Gallery plugin for WordPress

Automatic YouTube Gallery lets you create unlimited video galleries on your site. As the name suggests, this plugin automates most of the setup process so you can create a YouTube video gallery in minutes.

With this plugin, you can create a YouTube gallery using a channel ID, username, playlist, search terms, or even by entering specific video URLs. You can also embed live streams on your WordPress website.

After choosing your videos, Automatic YouTube Gallery lets you fine-tune how the gallery looks. This includes changing the number of columns in the gallery layout, changing the ratio, adding or removing the video titles, and more.

Automatic YouTube Gallery also has some settings to help keep visitors on your website for longer, such as autoplaying the next video and looping a single video. To make your YouTube gallery more accessible, you can also activate closed captions by default or change the language used by the video player’s interface.

With Automatic YouTube Gallery, you don’t have to worry about videos slowing down your site. The plugin has a caching feature that can reduce page loading times and boost your WordPress speed and performance.

This plugin is also fully compatible with WordPress multisite.

Pricing: You can download the Automatic YouTube Gallery plugin for free from the official WordPress repository.

8. All-in-One Video Gallery

The All in One Video Gallery WordPress plugin

You can use All-in-One Video Gallery to show videos from YouTube, Vimeo, Dailymotion, Rumble, and more in an HTML5 video player.

Unlike other plugins that have their own editors, All-in-One Video Gallery integrates with the familiar WordPress page and post editor. It also has video categories and tags that work exactly the same as the built-in WordPress categories and tags.

With this in mind, it’s very easy to get started with the All-in-One Video Gallery.

After adding your videos, All-in-One Video Gallery lets you customize the player controls, playback options, and video quality levels. This plugin also aims to make your videos more accessible by supporting WebVTT subtitles and showing the subtitles automatically.

The plugin is GDPR compliant and will get the visitor’s consent before loading YouTube, Vimeo, or embedded videos from third-party websites. If you prefer, then you can also disable the plugin’s cookies for added privacy.

When you’re ready, you can either show all your videos in a single gallery or create different galleries using the plugin’s categories feature. This allows you to create lots of different galleries, with different content.

Another option is embedding the built-in All-in-One Video Gallery search form. This allows visitors to search through all your videos, to find exactly the content they’re looking for.

Pricing: You can download All-in-One Video Gallery for free from the WordPress repository.

9. Video Gallery – Total Soft

The Video Gallery WordPress plugin

Video Gallery by Total Soft can add an unlimited number of professionally-designed video galleries to your website. This plugin supports YouTube plus other major video platforms such as Vimeo and Wistia.

When building a gallery, you can choose between 16 different themes and 9 layouts. If your YouTube channel has lots of videos, then you can use pagination to spread the content across multiple pages.

This plugin puts a strong focus on gallery thumbnails. Every time you add a video to a gallery, you can replace the default YouTube thumbnail with an image of your choice.

After that, Video Gallery will play different hover effects when the visitor moves their mouse over the thumbnail. This includes some advanced hover effects that animate the video’s title, description, and other elements. In this way, your gallery can catch the visitor’s attention before it starts playing videos.

Video Gallery allows you to show videos from different online platforms, and even self-hosted videos, in the same gallery.

After building a gallery, you can add it to any page, post, or widget-ready area using a shortcode. Video Gallery also provides a code snippet that adds the gallery to your WordPress theme. For more information, please see our guide on how to easily add custom code in WordPress.

Pricing: You can download Video Gallery for free from the official WordPress repository.

What is the Best YouTube Video Gallery Plugin for WordPress?

If you’re looking for the easiest way to add a gallery to WordPress, then Smash Balloon Youtube Feed Pro is our top pick.

This plugin guides you through the process of connecting YouTube and WordPress and then makes it easy to build a professionally-designed gallery. It also lets you fine-tune every part of the gallery so it perfectly suits your WordPress theme.

When you’re happy with how the gallery looks, you can add it to any page, post, or widget-ready area using either a shortcode or the plugin’s Feeds for YouTube block.

Unlike some other plugins on the list, your Smash Balloon gallery will update automatically as you publish new videos to YouTube. This means visitors will always see the latest videos, and you don’t have to worry about updating your gallery manually.

While Smash Balloon YouTube Feed Pro is our top choice, it is a premium plugin. If you’re looking for a free YouTube gallery plugin, then you may want to take a look at Feeds for YouTube instead.

While this plugin is missing some of Smash Balloon’s advanced features, it still has everything you need to create a YouTube video gallery for your WordPress website.

This makes Feeds for YouTube our top pick for anyone who is just getting started, has a limited budget, or just wants to embed a small number of videos in their WordPress website.

We hope this article helped you choose the best YouTube gallery plugins for WordPress. You may also want to see our list of best drag-and-drop WordPress page builders to create custom designs and our tutorial on how to create an email newsletter to keep your visitors updated with your new videos.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post 9 Best YouTube Video Gallery Plugins for WordPress first appeared on WPBeginner.

GUESSING GAME HERE

GUESSING GAME HERE

please help im new in this python programming

  1. This is a game that allows a user to play guessing one single number to try to win at a maximum of 3 attempts. The possible numbers to win must be stored in a list or tuple or dictionary.
  2. It must be able to store 6 randomly generated possible numbers (between 0-60, thus 1 to 59 ) to win from in a list or tuple or dictionary.
  3. A person playing the game must have only three chances of playing to win or loose
  4. One can win in the first or second or third attempt. The moment one had a correct guess, the game must then end telling someone that he or she has won and end the game.
  5. If you did not win in all three attempts, then the program must inform the user that he has lost the game.
  6. The game only allow some to play the game using numbers between 0-60, thus 1-59.
  7. To win is to play only anyone correct number from our six numbers stored in list or tuple or dictionary. Please note that, playing a number below 1 or above 59 intentionally or by mistake must be counted as an attempt and chance of playing the game. A TOUCH IS A MOVE!!
  8. Please Note: winning is playing one correct number of the 6 stored numbers.
  9. The application can allow the user to enter his or her name to play as the game is ready to start, and welcomes him or her. NB. The name of the user can be used during playing.
  10. As the game is getting ready to start, the program can give a user options 'OK' to continue playing the game or 'CANCEL' to exit the game playing the game.
  11. All the code must be wrapped within a function