From 66e703374c2aa3f687864ec548b019735a2bfc04 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Nov 30 2017 10:17:15 +0000
Subject: [PATCH 1/4] Modal: don't show an empty footer


---

diff --git a/hubs/static/client/app/components/Modal.js b/hubs/static/client/app/components/Modal.js
index 5a468f1..8b8cf34 100644
--- a/hubs/static/client/app/components/Modal.js
+++ b/hubs/static/client/app/components/Modal.js
@@ -57,9 +57,11 @@ export default class Modal extends React.Component {
                 <div className="modal-body">
                   {this.props.children}
                 </div>
-                <div className="modal-footer">
-                  {this.props.footer}
-                </div>
+                { this.props.footer &&
+                  <div className="modal-footer">
+                    {this.props.footer}
+                  </div>
+                }
             </div>
           </div>
         </div>

From 7813d181fb0992de4d682fc4d3841290e2cb4b1c Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Nov 30 2017 10:17:15 +0000
Subject: [PATCH 2/4] Library widget rework


Fixes #433

---

diff --git a/hubs/static/client/app/core/utils.js b/hubs/static/client/app/core/utils.js
index 4499b04..f3ee5bd 100644
--- a/hubs/static/client/app/core/utils.js
+++ b/hubs/static/client/app/core/utils.js
@@ -3,7 +3,7 @@ import fetch from 'isomorphic-fetch';
 import universal from 'react-universal-component'
 
 
-export function backendCall(url, fetchConfig, json=false) {
+export function backendCall(url, fetchConfig) {
   console.log("Calling backend:", url, fetchConfig);
   const baseFetchConfig = {
     credentials: "same-origin",
@@ -12,10 +12,12 @@ export function backendCall(url, fetchConfig, json=false) {
   fetchConfig = Object.assign({}, baseFetchConfig, fetchConfig);
   return fetch(url, {...baseFetchConfig, ...fetchConfig}).then(
     (response) => {
-      let resultPromise = json ? response.json() : response.text();
+      const contentType = response.headers.get("content-type");
+      const isJson = (contentType && contentType.includes("application/json"))
+      let resultPromise = isJson ? response.json() : response.text();
       if (!response.ok) {
         resultPromise = resultPromise.then((content) => {
-          if (json) {
+          if (isJson) {
             // It's an object describing the error.
             throw content;
           } else {
@@ -30,11 +32,15 @@ export function backendCall(url, fetchConfig, json=false) {
 }
 
 
-export function apiCall(url, fetchConfig) {
-  return backendCall(url, fetchConfig, true).then(
+export function apiCall(url, fetchConfig, extractData=true) {
+  return backendCall(url, fetchConfig).then(
     result => {
       if (result.status === "OK") {
-        return result.data;
+        if (extractData) {
+          return result.data;
+        } else {
+          return result;
+        }
       } else {
         throw new Error(result.message);
       }
diff --git a/hubs/static/client/app/widgets/library/AddLinkDialog.js b/hubs/static/client/app/widgets/library/AddLinkDialog.js
new file mode 100644
index 0000000..f8c4c30
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/AddLinkDialog.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { apiCall } from '../../core/utils';
+import Modal from '../../components/Modal';
+import SelectLinkForm from "./SelectLinkForm";
+import EditLinkForm from "./EditLinkForm";
+
+
+export default class AddLinkDialog extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      linkData: undefined,
+      error: null,
+      isLoading: false,
+    };
+    this.selectUrl = this.selectUrl.bind(this);
+    this.fetchLinkData = this.fetchLinkData.bind(this);
+    this.addLink = this.addLink.bind(this);
+  }
+
+  selectUrl(url) {
+    this.fetchLinkData(url);
+  }
+
+  fetchLinkData(url) {
+    const serverUrl = `${this.props.url}?url=${url}`;
+    this.setState({isLoading: true});
+    apiCall(serverUrl).then(
+      (linkData) => {
+        this.setState({
+          linkData, isLoading: false});
+      },
+      (error) => {
+        this.setState({
+          error: error.message, isLoading: false});
+      }
+    );
+  }
+
+  addLink(link) {
+    this.props.addLink(link);
+  }
+
+  render() {
+    let content = null;
+    if (!this.state.linkData) {
+      content = (
+        <SelectLinkForm
+          onSelect={this.selectUrl}
+          isLoading={this.state.isLoading}
+          error={this.state.error}
+          />
+      );
+    } else {
+      content = (
+        <EditLinkForm
+          onSubmit={this.addLink}
+          isLoading={this.isLoading}
+          error={this.props.addError}
+          link={this.state.linkData}
+          submitText="Add link"
+          />
+      );
+    }
+    return (
+      <Modal
+        title="Add a link"
+        onCloseClicked={this.props.onCloseClicked}
+        isLarge={Boolean(this.state.linkData)}
+        cssClass="AddLink"
+        >
+        {content}
+      </Modal>
+    );
+  }
+}
diff --git a/hubs/static/client/app/widgets/library/Config.js b/hubs/static/client/app/widgets/library/Config.js
new file mode 100644
index 0000000..2aca0b2
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/Config.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import SimpleWidgetConfig from '../../components/SimpleWidgetConfig';
+
+
+// Use the default configuration, but filter out the "links" parameter.
+
+export default function Config(props) {
+  const widgetParams = props.widget.params.filter((param) => (param.name !== "links"));
+  const filteredProps = {
+    ...props,
+    widget: {
+      ...props.widget,
+      params: widgetParams,
+    }
+  };
+  return (
+    <SimpleWidgetConfig {...filteredProps} />
+  );
+}
diff --git a/hubs/static/client/app/widgets/library/EditLinkDialog.js b/hubs/static/client/app/widgets/library/EditLinkDialog.js
new file mode 100644
index 0000000..cd4a990
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/EditLinkDialog.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Modal from '../../components/Modal';
+import EditLinkForm from "./EditLinkForm";
+
+
+export default class EditLinkDialog extends React.Component {
+
+  render() {
+    if (!this.props.link) {
+      return null;
+    }
+    return (
+      <Modal
+        title="Edit link"
+        onCloseClicked={this.props.onCloseClicked}
+        isLarge={true}
+        cssClass="EditLink"
+        >
+        <EditLinkForm
+          onSubmit={this.props.editLink}
+          error={this.props.error}
+          link={this.props.link}
+          submitText="Edit link"
+          />
+      </Modal>
+    );
+  }
+}
diff --git a/hubs/static/client/app/widgets/library/EditLinkForm.js b/hubs/static/client/app/widgets/library/EditLinkForm.js
new file mode 100644
index 0000000..637d370
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/EditLinkForm.js
@@ -0,0 +1,100 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Spinner from "../../components/Spinner";
+
+
+export default class EditLinkForm extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.state = this.props.link;
+    this.onChange = this.onChange.bind(this);
+    this.onSubmit = this.onSubmit.bind(this);
+  }
+
+  onChange(e) {
+    const name = e.target.name;
+    const value = e.target.value;
+    this.setState({[name]: value});
+  }
+
+  onSubmit(e) {
+    e.preventDefault();
+    this.props.onSubmit(this.state);
+  }
+
+  render() {
+    return (
+      <form
+        onSubmit={this.onSubmit}
+        >
+        <div className="row">
+          <div className="col-12 text-truncate">
+            <span title={this.state.url}>
+              {this.state.url}
+            </span>
+          </div>
+        </div>
+        <div className="row">
+          <div className="col-3">
+            {this.state.image ?
+              <img
+                alt="preview"
+                src={this.state.image}
+                title="Preview"
+                className="link-preview border border-secondary mt-2"
+                />
+              :
+              <p className="text-center text-secondary">
+                <em>(no image preview)</em>
+              </p>
+            }
+          </div>
+          <div className="col-9">
+            <div className="form-group">
+              <label className="col-form-label" htmlFor="title">
+                Title
+              </label>
+              <input
+                type="text"
+                className="form-control"
+                name="title"
+                value={this.state.title}
+                onChange={this.onChange}
+                />
+            </div>
+            <div className="form-group">
+              <label className="col-form-label" htmlFor="description">
+                Description
+              </label>
+              <textarea
+                name="description"
+                className="form-control"
+                value={this.state.description}
+                rows="3"
+                onChange={this.onChange}
+                />
+            </div>
+            <div className="form-group">
+              <button
+                className="btn btn-primary"
+                type="submit"
+                disabled={this.props.isLoading}
+                >
+                {this.props.submitText}
+              </button>
+              { this.props.isLoading &&
+                <Spinner />
+              }
+            </div>
+          </div>
+        </div>
+        { this.props.error &&
+            <div className="alert alert-warning">
+              {this.props.error}
+            </div>
+        }
+      </form>
+    );
+  }
+}
diff --git a/hubs/static/client/app/widgets/library/Link.js b/hubs/static/client/app/widgets/library/Link.js
new file mode 100644
index 0000000..6294efb
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/Link.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+
+export default class Link extends React.Component {
+
+  render() {
+    return (
+      <li className="Link media py-2">
+        <div className="mr-2 link-preview">
+          <img
+            className="link-preview mt-2"
+            src={this.props.image}
+            />
+        </div>
+        <div className={`media-body ${this.props.showDropdown ? "pr-3" : ""}`}>
+          { this.props.showDropdown &&
+            <div
+              className="dropdown"
+              >
+              <a
+                className="text-secondary dropdown-toggle"
+                href="#"
+                role="button"
+                data-toggle="dropdown"
+                aria-haspopup="true"
+                aria-expanded="false"
+                >
+              </a>
+              <div
+                className="dropdown-menu dropdown-menu-right"
+                aria-labelledby="dropdownMenuLink"
+                >
+                <a
+                  className="dropdown-item"
+                  href="#"
+                  onClick={this.props.onEdit}
+                  >
+                  Edit item
+                </a>
+                <a
+                  className="dropdown-item"
+                  href="#"
+                  onClick={this.props.onDelete}
+                  >
+                  Delete
+                </a>
+              </div>
+            </div>
+          }
+          <a
+            href={this.props.url}
+            className="d-block"
+            target="_blank"
+            >
+            {this.props.title}
+          </a>
+          <p className="mb-0 description">
+            <small>
+              {this.props.description}
+            </small>
+          </p>
+        </div>
+      </li>
+    );
+  }
+}
+Link.propTypes = {
+  showDropDown: PropTypes.bool,
+  url: PropTypes.string.isRequired,
+  title: PropTypes.string.isRequired,
+  description: PropTypes.string,
+  onEdit: PropTypes.func,
+  onDelete: PropTypes.func,
+}
+Link.defaultProps = {
+  showDropDown: false,
+  description: null,
+}
diff --git a/hubs/static/client/app/widgets/library/SelectLinkForm.js b/hubs/static/client/app/widgets/library/SelectLinkForm.js
new file mode 100644
index 0000000..bc7db75
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/SelectLinkForm.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Spinner from "../../components/Spinner";
+
+
+export default class SelectLinkForm extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      url: "",
+    };
+    this.onInputChange = this.onInputChange.bind(this);
+    this.onSelect = this.onSelect.bind(this);
+  }
+
+  onInputChange(e) {
+    const value = e.target.value;
+    this.setState({url: value});
+  }
+
+  onSelect(e) {
+    e.preventDefault();
+    this.props.onSelect(this.state.url);
+  }
+
+  render() {
+    return (
+      <div>
+        <form className="form-inline" onSubmit={this.onSelect}>
+          <label className="sr-only" htmlFor="url">URL</label>
+          <input
+            type="text"
+            name="url"
+            className="form-control"
+            autoFocus={true}
+            placeholder="Enter an URL..."
+            value={this.state.url}
+            onChange={this.onInputChange}
+            />
+          <button
+            className="btn btn-primary mx-3"
+            type="submit"
+            disabled={this.props.isLoading}
+            >
+            Fetch URL
+          </button>
+          { this.props.isLoading &&
+            <Spinner circle={true} />
+          }
+        </form>
+        { this.props.error &&
+          <div className="alert alert-warning mt-3">
+            {this.props.error}
+          </div>
+        }
+      </div>
+    );
+  }
+}
diff --git a/hubs/static/client/app/widgets/library/Widget.js b/hubs/static/client/app/widgets/library/Widget.js
new file mode 100644
index 0000000..dc90489
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/Widget.js
@@ -0,0 +1,244 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { apiCall } from '../../core/utils';
+import WidgetChrome from '../../components/WidgetChrome';
+import "./library.css";
+import Link from "./Link";
+import AddLinkDialog from "./AddLinkDialog";
+import EditLinkDialog from "./EditLinkDialog";
+import Pagination from "../../components/Pagination";
+
+
+class LibraryWidget extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      links: [],
+      page: null,
+      error: null,
+      isLoading: false,
+      isAddDialogOpened: false,
+      editingLink: false,
+    };
+    this.loadFromServer = this.loadFromServer.bind(this);
+    this.changePage = this.changePage.bind(this);
+    this.onAddDialogOpen = this.onAddDialogOpen.bind(this);
+    this.onAddDialogClose = this.onAddDialogClose.bind(this);
+    this.addLink = this.addLink.bind(this);
+    this.onEditDialogClose = this.onEditDialogClose.bind(this);
+    this.saveEditedLink = this.saveEditedLink.bind(this);
+    this.clearError = this.clearError.bind(this);
+  }
+
+  componentDidMount() {
+    if (!this.props.editMode) {
+      this.loadFromServer();
+    }
+  }
+
+  loadFromServer(pageNr=1) {
+    const url = `${this.props.widget.urls.data}?page=${pageNr}`;
+    apiCall(url, {}, false).then(
+      (result) => {
+        this.setState({
+          links: result.data,
+          page: result.page,
+          error: null,
+        });
+      },
+      (error) => {
+        console.log(error);
+        this.setState({
+          error: error.message
+        });
+      }
+    );
+  }
+
+  changePage(pageNr) {
+    console.log(pageNr);
+    this.loadFromServer(pageNr);
+  }
+
+  onAddDialogOpen(e) {
+    this.setState({isAddDialogOpened: true});
+  }
+
+  onAddDialogClose(e) {
+    this.setState({isAddDialogOpened: false});
+  }
+
+  addLink(link) {
+    const body = JSON.stringify(link);
+    apiCall(this.props.widget.urls.data, {method: "POST", body}).then(
+      (result) => {
+        this.setState((prevState, props) => ({
+            links: [...prevState.links, link], // Optimistic update.
+            isAddDialogOpened: false,
+            error: null,
+          })
+        );
+        this.loadFromServer();
+      },
+      (error) => {
+        console.log(error);
+        this.setState({
+          error: error.message
+        });
+      }
+    );
+  }
+
+  editLink(index, e) {
+    e.preventDefault();
+    this.setState((prevState, props) => ({
+      editingLink: prevState.links[index]
+    }));
+  }
+
+  onEditDialogClose(e) {
+    this.setState({editingLink: false});
+  }
+
+  saveEditedLink(link) {
+    const body = JSON.stringify(link);
+    apiCall(this.props.widget.urls.data, {method: "PUT", body}).then(
+      (result) => {
+        this.setState((prevState, props) => ({
+            links: prevState.links.map((l) => {
+              if (l.url === link.url) {
+                return link;
+              }
+              return l;
+            }),
+            editingLink: false,
+            error: null,
+          })
+        );
+        this.loadFromServer();
+      },
+      (error) => {
+        console.log(error);
+        this.setState({
+          error: error.message
+        });
+      }
+    );
+  }
+
+  deleteLink(index, e) {
+    e.preventDefault();
+    const link = this.state.links[index];
+    const body = JSON.stringify(link);
+    apiCall(this.props.widget.urls.data, {method: "DELETE", body}).then(
+      (result) => {
+        this.setState((prevState, props) => ({
+            links: prevState.links.filter((l) => (l.url !== link.url)),
+            editingLink: false,
+          })
+        );
+        this.loadFromServer();
+      },
+      (error) => {
+        console.log(error);
+        this.setState({
+          error: error.message
+        });
+      }
+    );
+  }
+
+  clearError(e) {
+    e.preventDefault();
+    this.setState({error: null});
+  }
+
+  render() {
+    const links = this.state.links.map((link, index) => (
+      <Link
+        key={link.url}
+        {...link}
+        onEdit={this.editLink.bind(this, index)}
+        onDelete={this.deleteLink.bind(this, index)}
+        showDropdown={this.props.currentUser.perms.config_hub}
+        />
+      )
+    );
+    return (
+      <WidgetChrome
+        widget={this.props.widget}
+        editMode={this.props.editMode}
+        >
+        <div className="widget-library p-2">
+          { this.state.error &&
+            <div className="alert alert-warning">
+              {this.state.error}
+              <button
+                type="button"
+                className="close"
+                aria-label="Close"
+                onClick={this.clearError}
+                >
+                <span aria-hidden="true">&times;</span>
+              </button>
+            </div>
+          }
+          { links.length > 0 ?
+            <div>
+              <ul className="list-unstyled">
+                {links}
+              </ul>
+              { this.state.page.total_pages > 1 &&
+                <Pagination
+                  page={this.state.page}
+                  onPageChange={this.changePage}
+                  className="mt-2 justify-content-center pagination-sm"
+                  />
+              }
+            </div>
+            :
+            <p className="text-secondary">
+              <em>No links yet.</em>
+            </p>
+          }
+          {this.props.currentUser.perms.config_hub &&
+            <div>
+              <div className="text-center">
+                <button
+                  className="btn btn-secondary btn-sm"
+                  onClick={this.onAddDialogOpen}
+                  >
+                  Add a New Item
+                </button>
+              </div>
+              {this.state.isAddDialogOpened &&
+                <AddLinkDialog
+                  onCloseClicked={this.onAddDialogClose}
+                  addLink={this.addLink}
+                  addError={this.state.error}
+                  url={this.props.widget.urls.getLink}
+                  />
+              }
+            </div>
+          }
+          <EditLinkDialog
+            link={this.state.editingLink}
+            editLink={this.saveEditedLink}
+            onCloseClicked={this.onEditDialogClose}
+            error={this.props.error}
+            />
+        </div>
+      </WidgetChrome>
+    );
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    currentUser: state.currentUser,
+  }
+};
+
+export default connect(mapStateToProps)(LibraryWidget);
diff --git a/hubs/static/client/app/widgets/library/library.css b/hubs/static/client/app/widgets/library/library.css
new file mode 100644
index 0000000..54dc556
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/library.css
@@ -0,0 +1,21 @@
+.widget-library div.link-preview {
+  max-width: 25%;
+}
+.widget-library img.link-preview {
+  width: 100%;
+  display: inline-block;
+}
+
+
+.widget-library .Link {
+    border-bottom: 1px solid #eee;
+}
+
+.widget-library .Link .dropdown {
+    position: absolute;
+    right: .5em;
+}
+
+.widget-library .Link .description {
+    line-height: 1;
+}
diff --git a/hubs/tests/widgets/test_library.py b/hubs/tests/widgets/test_library.py
index 6cb8297..99cb9c1 100644
--- a/hubs/tests/widgets/test_library.py
+++ b/hubs/tests/widgets/test_library.py
@@ -1,26 +1,73 @@
 from __future__ import unicode_literals
 
+import json
+
+from hubs.app import app
 from hubs.tests import FakeAuthorization, widget_instance
 from . import WidgetTest
 
 
 class TestLibrary(WidgetTest):
     plugin = 'library'  # The name in hubs.widgets.registry
+    maxDiff = None
 
     def test_data_simple(self):
         widget = widget_instance('ralph', self.plugin)
         user = FakeAuthorization('ralph')
         response = self.check_url(
-            '/ralph/w/%s/%i/' % (self.plugin, widget.idx), user)
+            '/ralph/w/%s/%i/data' % (self.plugin, widget.idx), user)
         expected_dict = {
-            "urls": [
-                "<a href=\"ralph/\">ralph/</a>"
-            ],
-            'edit_mode': False,
-            'widget': widget.module,
-            'widget_instance': widget,
+            'data': [],
+            'page': {
+                'has_next': False,
+                'has_prev': False,
+                'nr': 1,
+                'total_entries': 0,
+                'total_pages': 1
+            },
+            'status': 'OK',
         }
-        self.assertDictEqual(response.context, expected_dict)
+        data = json.loads(response.get_data(as_text=True))
+        self.assertDictEqual(data, expected_dict)
 
-    def test_view_authz(self):
-        self._test_view_authz()
+    def test_props(self):
+        widget = widget_instance('ralph', self.plugin)
+        with app.test_request_context('/'):
+            props = widget.get_props()
+        self.assertDictEqual(props, {
+            'component': 'library',
+            'config': {'links': [], 'per_page': 4},
+            'cssClass': None,
+            'hiddenIfEmpty': False,
+            'idx': widget.idx,
+            'index': 51,
+            'isReact': True,
+            'isLarge': False,
+            'label': 'Library',
+            'name': 'library',
+            'params': [
+                {'default': [],
+                 'help': 'A JSON list of dicts with `url`, `title`, '
+                         '`description`, and `image` keys.',
+                 'label': 'Links',
+                 'name': 'links',
+                 'renderTag': 'input',
+                 'renderAttributes': {'type': 'text'},
+                 },
+                {'default': 4,
+                 'help': 'The number of links per page to display.',
+                 'label': 'Links per page',
+                 'name': 'per_page',
+                 'renderTag': 'input',
+                 'renderAttributes': {'type': 'text'},
+                 },
+            ],
+            'position': 'right',
+            'selfUrl': '/api/hubs/ralph/widgets/{}/'.format(widget.idx),
+            'title': 'Library',
+            'urls': {
+                'data': '/ralph/w/library/{}/data'.format(widget.idx),
+                'getLink': '/ralph/w/library/{}/get-link'.format(
+                    widget.idx),
+            }
+        })
diff --git a/hubs/utils/views.py b/hubs/utils/views.py
index c42356a..a769dc8 100644
--- a/hubs/utils/views.py
+++ b/hubs/utils/views.py
@@ -143,8 +143,10 @@ def configure_widget_instance(widget_instance, widget_config):
     for param in widget_instance.module.get_parameters():
         val = widget_config.get(param.name)
         if not val:
-            raise WidgetConfigError(
-                'You must provide a value for: %s' % param.name)
+            if param.default is None:
+                raise WidgetConfigError(
+                    'You must provide a value for: %s' % param.name)
+            val = param.default
         values[param.name] = val
     try:
         config = widget_instance.module.validate_parameters(values)
diff --git a/hubs/widgets/base.py b/hubs/widgets/base.py
index 55767bd..0155b0f 100644
--- a/hubs/widgets/base.py
+++ b/hubs/widgets/base.py
@@ -80,6 +80,7 @@ class Widget(object):
         """
         config = {}
         for param in self.get_parameters():
+            print(param.name, param.validator, values[param.name])
             config[param.name] = param.validator(values[param.name])
         return config
 
diff --git a/hubs/widgets/library/__init__.py b/hubs/widgets/library/__init__.py
index e8315d3..817de1c 100644
--- a/hubs/widgets/library/__init__.py
+++ b/hubs/widgets/library/__init__.py
@@ -1,32 +1,40 @@
 from __future__ import unicode_literals
 
-from hubs.widgets import clean_input, validators
+import flask
+
+from hubs.widgets import validators
 from hubs.widgets.base import Widget
-from hubs.widgets.view import RootWidgetView
 
 
 class Library(Widget):
 
     name = "library"
     position = "both"
-    parameters = [dict(
-        name="urls",
-        label="URLs",
-        default=None,
-        validator=validators.Text,
-        help="A comma separated list of URLs to add to the library. "
-             "External links must include the whole link "
-             "(starting with http...)."
+    parameters = [
+        dict(
+            name="links",
+            label="Links",
+            default=[],
+            help="A JSON list of dicts with `url`, `title`, `description`, "
+                 "and `image` keys."
+        ), dict(
+            name="per_page",
+            label="Links per page",
+            default=4,
+            validator=validators.Integer,
+            help="The number of links per page to display.",
         )]
+    is_react = True
+    views_module = ".views"
 
-
-class BaseView(RootWidgetView):
-
-    def get_context(self, instance, *args, **kwargs):
-        urls = [
-            clean_input.clean(
-                '<a href="{0}">{0}</a>'.format(u.strip()))
-            for u in instance.config.get('urls', '').split(',')
-            if u.strip()
-        ]
-        return dict(urls=urls)
+    def get_props(self, instance, *args, **kwargs):
+        props = super(Library, self).get_props(instance, *args, **kwargs)
+        if instance is not None:
+            hub_name = instance.hub.name
+            props["urls"] = dict(
+                data=flask.url_for(
+                    "library_data", hub=hub_name, idx=instance.idx),
+                getLink=flask.url_for(
+                    "library_get_link", hub=hub_name, idx=instance.idx),
+                )
+        return props
diff --git a/hubs/widgets/library/templates/root.html b/hubs/widgets/library/templates/root.html
deleted file mode 100644
index 4cbff48..0000000
--- a/hubs/widgets/library/templates/root.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="rules-container">
-  <div class="row">
-    <ul>
-    {% for url in urls %}
-    <li>
-      {{ url }}
-    </li>
-    {% endfor %}
-    </ul>
-  </div>
-</div>
diff --git a/hubs/widgets/library/views.py b/hubs/widgets/library/views.py
new file mode 100644
index 0000000..1b338c2
--- /dev/null
+++ b/hubs/widgets/library/views.py
@@ -0,0 +1,111 @@
+from __future__ import unicode_literals
+
+import flask
+import requests
+from bs4 import BeautifulSoup
+
+from hubs.utils.pagination import paginate
+from hubs.widgets.view import WidgetView
+from hubs.utils.views import check_hub_access
+
+
+class LinkValidationError(Exception):
+
+    def __init__(self, message):
+        self.message = message
+
+
+def validate_link(link):
+    if not link["url"]:
+        raise LinkValidationError("The link must have an URL")
+    if not link["title"]:
+        raise LinkValidationError("The link must have a title")
+
+
+class DataView(WidgetView):
+
+    name = "data"
+    url_rules = ["data"]
+    methods = ['GET', 'POST', 'PUT', 'DELETE']
+    json = True
+
+    def get_context(self, instance, *args, **kwargs):
+        if flask.request.method in ["POST", "PUT", "DELETE"]:
+            # Updating in-place is not supported, it's a class property.
+            config = instance.config.copy()
+            try:
+                if flask.request.method == "POST":
+                    check_hub_access(instance.hub, "config", json=True)
+                    new_link = flask.request.get_json()
+                    validate_link(new_link)
+                    if new_link["url"] in [l["url"] for l in config["links"]]:
+                        raise LinkValidationError(
+                            "This URL is already in the library")
+                    config["links"].append(new_link)
+                elif flask.request.method == "PUT":
+                    check_hub_access(instance.hub, "config", json=True)
+                    link = flask.request.get_json()
+                    config["links"] = list(map(
+                        lambda l: link if l["url"] == link["url"] else l,
+                        config["links"]))
+                elif flask.request.method == "DELETE":
+                    check_hub_access(instance.hub, "config", json=True)
+                    link = flask.request.get_json()
+                    config["links"] = list(filter(
+                        lambda l: l["url"] != link["url"],
+                        config["links"]))
+            except LinkValidationError as e:
+                return dict(
+                    status="ERROR",
+                    message=e.message,
+                    )
+            instance.config = config
+            flask.g.db.commit()
+            return {"status": "OK"}
+        links, page_data = paginate(
+            instance.config["links"], instance.config["per_page"])
+        return dict(status="OK", data=links, page=page_data)
+
+
+class GetLinkView(WidgetView):
+    """XXX
+    """
+
+    name = "get_link"
+    url_rules = ["get-link"]
+    json = True
+
+    def get_context(self, instance, *args, **kwargs):
+        url = flask.request.values.get("url")
+        if not url:
+            return dict(status="ERROR", message="No URL.")
+        try:
+            response = requests.get(url, timeout=10)
+        except requests.exceptions.Timeout:
+            return dict(status="ERROR", message="Connection timed out.")
+        if not response.ok:
+            return dict(
+                status="ERROR",
+                message="Could not fetch this URL: error {}.".format(
+                    response.status_code)
+                )
+        soup = BeautifulSoup(response.text, 'html.parser')
+        # Title
+        title = soup.head.find("meta", property="og:title")
+        if title is None:
+            title = soup.head.title.string
+        else:
+            title = title["content"]
+        # Description
+        desc = soup.head.find("meta", property="og:description")
+        if desc is None:
+            desc = soup.head.find("meta", name="description")
+        else:
+            desc = desc["content"]
+        # Image
+        image = soup.head.find("meta", property="og:image")
+        if image is not None:
+            image = image["content"]
+        return dict(status="OK", data=dict(
+            url=url, title=title, description=desc, image=image,
+        ))
diff --git a/requirements.txt b/requirements.txt
index 95be6f4..df9f7c8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
 alembic
 arrow
+beautifulsoup4
 bleach<2.0.0
 blinker
 python-dateutil

From 493d7ccff6a026359c58a973b12b8aa1bb897c43 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Nov 30 2017 10:17:15 +0000
Subject: [PATCH 3/4] Library widget: add a placeholder image


---

diff --git a/hubs/static/client/app/widgets/library/EditLinkForm.js b/hubs/static/client/app/widgets/library/EditLinkForm.js
index 637d370..b18d82f 100644
--- a/hubs/static/client/app/widgets/library/EditLinkForm.js
+++ b/hubs/static/client/app/widgets/library/EditLinkForm.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Spinner from "../../components/Spinner";
+import MissingThumb from "./library-missing-thumb-large.png";
 
 
 export default class EditLinkForm extends React.Component {
@@ -37,18 +38,12 @@ export default class EditLinkForm extends React.Component {
         </div>
         <div className="row">
           <div className="col-3">
-            {this.state.image ?
-              <img
-                alt="preview"
-                src={this.state.image}
-                title="Preview"
-                className="link-preview border border-secondary mt-2"
-                />
-              :
-              <p className="text-center text-secondary">
-                <em>(no image preview)</em>
-              </p>
-            }
+            <img
+              alt="preview"
+              src={this.state.image || MissingThumb}
+              title="Preview"
+              className="link-preview border border-secondary mt-2"
+              />
           </div>
           <div className="col-9">
             <div className="form-group">
diff --git a/hubs/static/client/app/widgets/library/Link.js b/hubs/static/client/app/widgets/library/Link.js
index 6294efb..842d201 100644
--- a/hubs/static/client/app/widgets/library/Link.js
+++ b/hubs/static/client/app/widgets/library/Link.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import MissingThumb from "./library-missing-thumb.png";
 
 
 export default class Link extends React.Component {
@@ -10,7 +11,7 @@ export default class Link extends React.Component {
         <div className="mr-2 link-preview">
           <img
             className="link-preview mt-2"
-            src={this.props.image}
+            src={this.props.image || MissingThumb}
             />
         </div>
         <div className={`media-body ${this.props.showDropdown ? "pr-3" : ""}`}>
diff --git a/hubs/static/client/app/widgets/library/Widget.js b/hubs/static/client/app/widgets/library/Widget.js
index dc90489..f07d104 100644
--- a/hubs/static/client/app/widgets/library/Widget.js
+++ b/hubs/static/client/app/widgets/library/Widget.js
@@ -58,7 +58,6 @@ class LibraryWidget extends React.Component {
   }
 
   changePage(pageNr) {
-    console.log(pageNr);
     this.loadFromServer(pageNr);
   }
 
diff --git a/hubs/static/client/app/widgets/library/library-missing-thumb-large.png b/hubs/static/client/app/widgets/library/library-missing-thumb-large.png
new file mode 100644
index 0000000..b0db4a4
Binary files /dev/null and b/hubs/static/client/app/widgets/library/library-missing-thumb-large.png differ
diff --git a/hubs/static/client/app/widgets/library/library-missing-thumb.png b/hubs/static/client/app/widgets/library/library-missing-thumb.png
new file mode 100644
index 0000000..2fc2021
Binary files /dev/null and b/hubs/static/client/app/widgets/library/library-missing-thumb.png differ
diff --git a/hubs/static/client/app/widgets/library/library-missing-thumb.svg b/hubs/static/client/app/widgets/library/library-missing-thumb.svg
new file mode 100644
index 0000000..8d0eb0a
--- /dev/null
+++ b/hubs/static/client/app/widgets/library/library-missing-thumb.svg
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="72"
+   height="72"
+   viewBox="0 0 0.75 0.75"
+   version="1.1"
+   id="svg80330"
+   sodipodi:docname="library-item-no-thumb-filler.svg"
+   inkscape:version="0.92.2 5c3e80d, 2017-08-06">
+  <defs
+     id="defs80323" />
+  <sodipodi:namedview
+     inkscape:document-units="px"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="68.999847"
+     inkscape:cy="-247.61716"
+     inkscape:current-layer="layer1"
+     id="namedview80325"
+     showgrid="false"
+     inkscape:window-width="1280"
+     inkscape:window-height="1376"
+     inkscape:window-x="1280"
+     inkscape:window-y="27"
+     inkscape:window-maximized="0"
+     units="px"
+     width="1080px"
+     inkscape:pagecheckerboard="false"
+     objecttolerance="2"
+     inkscape:snap-path-clip="false"
+     inkscape:snap-path-mask="false"
+     gridtolerance="2"
+     guidetolerance="10"
+     inkscape:snap-perpendicular="true"
+     inkscape:snap-tangential="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata80327">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Máirín Duffy &lt;duffy@redhat.com&gt;</dc:title>
+          </cc:Agent>
+        </dc:creator>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-8.5833349,2.9855955)">
+    <rect
+       inkscape:export-ydpi="226.66672"
+       inkscape:export-xdpi="226.66672"
+       inkscape:export-filename="/home/duffy/library-missing-thumb.png"
+       y="-2.9855955"
+       x="8.5833349"
+       height="0.75"
+       width="0.75"
+       id="rect817585"
+       style="opacity:1;fill:#58595b;fill-opacity:1;stroke:none;stroke-width:0.01041667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952" />
+    <g
+       id="use819155"
+       style="fill:#e7e8e9;fill-opacity:1;stroke-width:0.01486988"
+       transform="matrix(0.00729709,0,0,0.00729709,8.6781267,-2.9375051)">
+      <title
+         id="title918616">Globe</title>
+      <path
+         inkscape:connector-curvature="0"
+         id="path918618"
+         d="m 768,1408 q 209,0 385.5,-103 Q 1330,1202 1433,1025.5 1536,849 1536,640 1536,431 1433,254.5 1330,78 1153.5,-25 977,-128 768,-128 559,-128 382.5,-25 206,78 103,254.5 0,431 0,640 0,849 103,1025.5 206,1202 382.5,1305 559,1408 768,1408 Z m 274,-521 q -2,-1 -9.5,-9.5 -7.5,-8.5 -13.5,-9.5 2,0 4.5,5 2.5,5 5,11 2.5,6 3.5,7 6,7 22,15 14,6 52,12 34,8 51,-11 -2,2 9.5,13 11.5,11 14.5,12 3,2 15,4.5 12,2.5 15,7.5 l 2,22 q -12,-1 -17.5,7 -5.5,8 -6.5,21 0,-2 -6,-8 0,7 -4.5,8 -4.5,1 -11.5,-1 -7,-2 -9,-1 -10,3 -15,7.5 -5,4.5 -8,16.5 -3,12 -4,15 -2,5 -9.5,10.5 -7.5,5.5 -9.5,10.5 -1,2 -2.5,5.5 -1.5,3.5 -3,6.5 -1.5,3 -4,5.5 -2.5,2.5 -5.5,2.5 -3,0 -7,-5 -4,-5 -7.5,-10 -3.5,-5 -4.5,-5 -3,2 -6,1.5 -3,-0.5 -4.5,-1 -1.5,-0.5 -4.5,-3 -3,-2.5 -5,-3.5 -3,-2 -8.5,-3 -5.5,-1 -8.5,-2 15,5 -1,11 -10,4 -16,3 9,4 7.5,12 -1.5,8 -8.5,14 h 5 q -1,4 -8.5,8.5 -7.5,4.5 -17.5,8.5 -10,4 -13,6 -8,5 -34,9.5 -26,4.5 -33,0.5 -5,-6 -4.5,-10.5 0.5,-4.5 4,-14 3.5,-9.5 3.5,-12.5 1,-6 -5.5,-13 -6.5,-7 -6.5,-12 0,-7 14,-15.5 14,-8.5 10,-21.5 -3,-8 -16,-16 -13,-8 -16,-12 -5,-8 -1.5,-18.5 Q 907,958 914,952 q 2,-2 1.5,-4 -0.5,-2 -3.5,-4.5 -3,-2.5 -5.5,-4 Q 904,938 900,936 l -3,-2 q -11,-5 -20.5,6 -9.5,11 -13.5,26 -7,25 -16,30 -23,8 -29,-1 -5,13 -41,26 -25,9 -58,4 6,1 0,15 -7,15 -19,12 3,6 4,17.5 1,11.5 1,13.5 3,13 12,23 1,1 7,8.5 6,7.5 9.5,13.5 3.5,6 0.5,6 35,-4 50,11 5,5 11.5,17 6.5,12 10.5,17 9,6 14,5.5 5,-0.5 14.5,-5.5 9.5,-5 14.5,-5 14,-1 15.5,11 1.5,12 -7.5,20 12,-1 3,17 -5,7 -8,9 -12,4 -27,-5 -8,-4 2,-8 -1,1 -9.5,-10.5 Q 809,1196 801,1190 q -8,-6 -16,5 -1,1 -5.5,13.5 -4.5,12.5 -9.5,13.5 -8,0 -16,-15 3,8 -11,15 -14,7 -24,8 19,12 -8,27 -7,4 -20.5,5 -13.5,1 -19.5,-4 -5,-7 -5.5,-11.5 -0.5,-4.5 5,-8 5.5,-3.5 10.5,-5.5 5,-2 11.5,-4 6.5,-2 8.5,-3 14,-10 8,-14 -2,-1 -8.5,-3.5 -6.5,-2.5 -11.5,-4.5 -5,-2 -6,-4 -3,-4 0,-14 3,-10 -2,-14 -5,5 -9,17.5 -4,12.5 -7,16.5 7,-9 -25,-6 l -10,1 q -4,0 -16,-2 -12,-2 -20.5,-1 -8.5,1 -13.5,8 -4,8 0,20 1,4 4,2 -4,3 -11,9.5 -7,6.5 -10,8.5 -46,-15 -94,-41 6,-1 12,1 5,2 13,6.5 8,4.5 10,5.5 34,14 42,7 l 5,5 q 14,-16 20,-25 -7,4 -30,1 -20,-6 -22,-12 7,-12 5,-18 -4,3 -11.5,10 -7.5,7 -14.5,11 -7,4 -15,5 -16,0 -22,-1 -146,-80 -235,-222 7,-7 12,-8 4,-1 5,-9 1,-8 2.5,-11 1.5,-3 11.5,3 9,-8 3,-19 1,1 44,-27 19,-17 21,-21 3,-11 -10,-18 -1,2 -9,9 -8,7 -9,4 -3,-5 0.5,-18.5 Q 301,850 308,851 301,851 298.5,835 296,819 296,799.5 296,780 295,776 l 2,-1 q -3,-12 5.5,-34.5 Q 311,718 324,721 q -13,-3 20,-43 6,-8 8,-9 3,-2 12,-7.5 9,-5.5 15,-10 6,-4.5 10,-10.5 4,-5 10,-22.5 6,-17.5 14,-23.5 -2,-6 9.5,-20 11.5,-14 10.5,-23 -1,0 -2.5,-1 -1.5,-1 -2.5,-1 3,-7 15.5,-14 12.5,-7 15.5,-13 1,-3 2,-10 1,-7 3,-11 2,-4 8,-2 2,20 -24,62 -15,25 -17,29 -3,5 -5.5,15.5 -2.5,10.5 -4.5,14.5 2,0 6,-1.5 4,-1.5 8.5,-3.5 4.5,-2 7.5,-4 3,-2 2,-3 -3,-7 2,-17.5 5,-10.5 12,-18.5 7,-8 17,-19 10,-11 12,-13 6,-6 14,-19.5 8,-13.5 0,-13.5 9,0 20,-10 11,-10 17,-20 5,-8 8,-26 3,-18 5,-24 2,-7 8.5,-13.5 Q 567,408 573,405 l 16,-8 q 0,0 13,-7 5,-2 18.5,-10.5 Q 634,371 642,368 q 10,-4 16,-4 6,0 14.5,2.5 8.5,2.5 13.5,3.5 15,2 29,-15 14,-17 21,-21 36,-19 55,-11 -2,-1 0.5,-7.5 2.5,-6.5 8,-15.5 5.5,-9 9,-14.5 3.5,-5.5 5.5,-8.5 5,-6 18,-15 13,-9 18,-15 6,4 7,9 -3,-8 7,-20 10,-12 18,-10 14,3 14,32 -31,-15 -49,18 0,1 -2.5,5.5 -2.5,4.5 -4,8.5 -1.5,4 -2.5,8.5 -1,4.5 0,7.5 1,3 5,3 9,0 10,3.5 1,3.5 -2,12.5 -3,9 -4,13 -1,8 -11,20 -10,12 -12,15 -5,-9 -16,-8 -11,1 -16,9 0,-1 -1.5,-5.5 -1.5,-4.5 -1.5,-6.5 -13,0 -15,1 1,3 2.5,17.5 1.5,14.5 3.5,22.5 1,4 5.5,12 4.5,8 7.5,14.5 3,6.5 4,12.5 1,6 -4.5,9.5 -5.5,3.5 -17.5,2.5 -19,-1 -26,-20 -1,-3 -3,-10.5 -2,-7.5 -5,-11.5 -3,-4 -9,-7 -7,-3 -24,-2 -17,1 -24,5 -13,8 -22.5,29 -9.5,21 -9.5,37 0,10 2.5,26.5 2.5,16.5 3,25 0.5,8.5 -5.5,24.5 3,2 9,9.5 6,7.5 10,10.5 2,1 4.5,1.5 2.5,0.5 4.5,0 2,-0.5 4,1.5 2,2 3,6 -1,1 -4,3 -3,3 -4,3 7,-3 28.5,1.5 21.5,4.5 27.5,-1.5 15,-11 22,2 0,1 -2.5,9.5 -2.5,8.5 -0.5,13.5 5,-27 29,-9 3,-3 15.5,-5 12.5,-2 17.5,-5 3,-2 7,-5.5 4,-3.5 5.5,-4.5 1.5,-1 5,0.5 3.5,1.5 8.5,6.5 10,-14 12,-24 11,-40 19,-44 7,-3 11,-2 4,1 4.5,9.5 0.5,8.5 0,14 Q 888,547 887,554 l -1,8 v 18 l -1,8 q -15,3 -18.5,12 -3.5,9 1.5,18.5 5,9.5 15,18.5 1,1 8,3.5 7,2.5 15.5,6.5 8.5,4 12.5,8 21,19 15,35 7,0 11,9 -1,0 -5,3 -4,3 -7.5,5 -3.5,2 -4.5,2 9,5 2,16 5,3 7.5,11 2.5,8 7.5,10 9,-12 21,-2 7,8 1,16 5,7 20.5,10.5 15.5,3.5 18.5,9.5 7,-2 8,2 1,4 1,12 0,8 3,12 4,5 15,9 11,4 13,5 l 17,11 q 3,4 0,4 18,-2 31,11 10,11 -6,20 3,6 -3,9.5 -6,3.5 -15,5.5 3,1 11.5,0.5 8.5,-0.5 10.5,1.5 15,10 -7,16 -17,5 -43,-12 z M 879,10 q 206,36 351,189 -3,3 -12.5,4.5 -9.5,1.5 -12.5,3.5 -18,7 -24,8 1,7 -2.5,13 -3.5,6 -8,9 -4.5,3 -12.5,8 -8,5 -11,7 -2,2 -7,6 -5,4 -7,5.5 -2,1.5 -7.5,4.5 -5.5,3 -8.5,2 -3,-1 -10,-1 l -3,-1 q -3,-1 -5.5,-2.5 -2.5,-1.5 -5.5,-3 -3,-1.5 -4,-3 -1,-1.5 0,-2.5 -21,17 -36,22 -5,1 -11,5.5 -6,4.5 -10.5,7 -4.5,2.5 -10,1.5 -5.5,-1 -11.5,-7 -5,-5 -6,-15 -1,-10 -2,-13 -7,5 0,17.5 7,12.5 2,18.5 -3,6 -10.5,4.5 -7.5,-1.5 -12,-4.5 -4.5,-3 -11.5,-8.5 -7,-5.5 -9,-6.5 -2,-1 -8.5,-5.5 -6.5,-4.5 -8.5,-7.5 -3,-4 -6,-12 -3,-8 -5,-11 -2,4 -11.5,6.5 -9.5,2.5 -9.5,5.5 2,-10 4,-35 2,-25 5,-38 7,-31 -12,-48 -27,-25 -29,-40 -4,-22 12,-26 0,-7 -8,-20.5 -8,-13.5 -7,-21.5 0,-6 2,-16 z"
+         transform="matrix(0.05,0,0,-0.05,0,76.8)"
+         style="stroke-width:0.01486988" />
+    </g>
+  </g>
+</svg>

From 10bbd45ee40766e9f27b5c3cc2c22b8054f256da Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Nov 30 2017 10:17:15 +0000
Subject: [PATCH 4/4] Library widget: fix the get-link view and test it


---

diff --git a/hubs/tests/vcr-request-data/hubs.tests.widgets.test_library.TestLibrary.test_get_link b/hubs/tests/vcr-request-data/hubs.tests.widgets.test_library.TestLibrary.test_get_link
new file mode 100644
index 0000000..a14c921
--- /dev/null
+++ b/hubs/tests/vcr-request-data/hubs.tests.widgets.test_library.TestLibrary.test_get_link
@@ -0,0 +1,771 @@
+interactions:
+- request:
+    body: null
+    headers:
+      Accept: ['*/*']
+      Accept-Encoding: ['gzip, deflate']
+      Connection: [keep-alive]
+      User-Agent: [python-requests/2.18.4]
+    method: GET
+    uri: http://fedoraproject.org/wiki/Design
+  response:
+    body:
+      string: !!binary |
+        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
+    headers:
+      accept-ranges: [bytes]
+      age: ['395956']
+      appserver: [proxy07.fedoraproject.org]
+      apptime: [D=7073]
+      cache-control: ['s-maxage=432000, must-revalidate, max-age=0']
+      connection: [Keep-Alive]
+      content-encoding: [gzip]
+      content-language: [en]
+      content-length: ['10650']
+      content-type: [text/html; charset=UTF-8]
+      date: ['Sat, 11 Nov 2017 02:13:08 GMT']
+      etag: ['W/"fpo-mediawiki-en_:pcache:idhash:20616-0!1!0!!en!2!edit=0--20170923153854-gzip"']
+      keep-alive: ['timeout=15, max=500']
+      last-modified: ['Fri, 13 Oct 2017 18:45:55 GMT']
+      referrer-policy: [same-origin]
+      server: [Apache/2.2.15 (Red Hat)]
+      vary: ['Accept-Encoding,Cookie,User-Agent']
+      via: [1.1 varnish-v4]
+      x-content-type-options: [nosniff, nosniff]
+      x-frame-options: [SAMEORIGIN]
+      x-powered-by: [PHP/5.3.3]
+      x-varnish: [2983663 1540191]
+      x-xss-protection: [1; mode=block]
+    status: {code: 200, message: OK}
+- request:
+    body: null
+    headers:
+      Accept: ['*/*']
+      Accept-Encoding: ['gzip, deflate']
+      Connection: [keep-alive]
+      User-Agent: [python-requests/2.18.4]
+    method: GET
+    uri: http://blog.linuxgrrl.com/
+  response:
+    body:
+      string: !!binary |
+        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
+        WgBY+sjpIDDVeIv/s2oxT/4FGg/saQWOAQA=
+    headers:
+      connection: [Keep-Alive]
+      content-encoding: [gzip]
+      content-type: [text/html; charset=UTF-8]
+      date: ['Wed, 15 Nov 2017 16:16:07 GMT']
+      keep-alive: ['timeout=2, max=100']
+      link: ['<http://blog.linuxgrrl.com/wp-json/>; rel="https://api.w.org/", <https://wp.me/1E19J>;
+          rel=shortlink']
+      server: [Apache]
+      vary: [Accept-Encoding]
+    status: {code: 200, message: OK}
+version: 1
diff --git a/hubs/tests/widgets/test_library.py b/hubs/tests/widgets/test_library.py
index 99cb9c1..7580c6d 100644
--- a/hubs/tests/widgets/test_library.py
+++ b/hubs/tests/widgets/test_library.py
@@ -71,3 +71,31 @@ class TestLibrary(WidgetTest):
                     widget.idx),
             }
         })
+
+    def test_get_link(self):
+        widget = widget_instance('ralph', self.plugin)
+        user = FakeAuthorization('ralph')
+        backend_url = "/ralph/w/{}/{}/get-link".format(self.plugin, widget.idx)
+        test_urls = [
+            {
+                'url': 'http://fedoraproject.org/wiki/Design',
+                'description': "",
+                'image': None,
+                'title': 'Design - FedoraProject',
+            }, {
+                'url': 'http://blog.linuxgrrl.com/',
+                'description': 'Open design forever.',
+                'image': 'https://s0.wp.com/i/blank.jpg',
+                'title': 'M\xe1ir\xedn Duffy',
+            },
+        ]
+        for test_url in test_urls:
+            response = self.check_url(
+                "{}?url={}".format(backend_url, test_url["url"]),
+                user)
+            expected_dict = {
+                'data': test_url,
+                'status': 'OK'
+            }
+            data = json.loads(response.get_data(as_text=True))
+            self.assertDictEqual(expected_dict, data)
diff --git a/hubs/widgets/library/views.py b/hubs/widgets/library/views.py
index 1b338c2..6a58de6 100644
--- a/hubs/widgets/library/views.py
+++ b/hubs/widgets/library/views.py
@@ -99,7 +99,7 @@ class GetLinkView(WidgetView):
         # Description
         desc = soup.head.find("meta", property="og:description")
         if desc is None:
-            desc = soup.head.find("meta", name="description")
+            desc = soup.head.find("meta", attrs={"name": "description"})
         else:
             desc = desc["content"]
         # Image
@@ -107,5 +107,8 @@ class GetLinkView(WidgetView):
         if image is not None:
             image = image["content"]
         return dict(status="OK", data=dict(
-            url=url, title=title, description=desc, image=image,
+            url=url,
+            title=title or "",
+            description=desc or "",
+            image=image,
         ))

