コード例 #1
0
def upload_images(img_map, media_url, oauth):
    """Upload images in the blog post to the Wordpress server
    """

    for filename, img in img_map.items():
        if img.get("target_path") is None:

            # Read local image data in
            with open(img.get("local_path"), "rb") as f:
                data = f.read()

            headers = {
                "Content-Type": mimetypes.guess_type(img.get("local_path"))[0],
                "Content-Disposition":
                "attachment; filename={}".format(filename),
            }
            # Upload image data with filename as specified in header
            response = requests.post(
                media_url,
                data=data,
                headers=headers,
                auth=oauth,
            )

            # Record the resulting URL from the image upload
            new_src = json.loads(response.text).get("guid").get("rendered")
            img_map[filename]["target_path"] = new_src
    program_log.debug("Updated image map after upload: {}".format(img_map))

    return img_map
コード例 #2
0
def parse_document(data, config, metadata, oauth):
    """Prepare the document to be published on Wordpress. This involves
    extracting the title, uploading the relevant images, and changing
    the links to these images to match those on the Wordpress site
    """
    program_log.debug("Converting content to html")
    html = pypandoc.convert_text(data, "html", format="md")
    soup = BeautifulSoup(html, "html.parser")
    title = extract_title(soup)
    program_log.debug("Extracted content title: {}".format(title))

    media_url = construct_url(
        base_url=config.get("base_url"),
        api_version=config.get("api_version"),
        endpoint="media",
    )
    program_log.debug("Constructed URL for media update: {}".format(media_url))
    img_map = get_image_replacement_map(soup, media_url, metadata.get("id"),
                                        oauth)
    img_map = upload_images(img_map, media_url, oauth)
    program_log.debug("Updating content with new image links")
    replace_image_links(soup, img_map)
    content = soup.encode(formatter="html5").lstrip()

    document = {"title": title, "content": content.decode("utf-8")}
    return document
コード例 #3
0
def get_existing_images(media_url, oauth):
    """Obtain a dictionary of all images currently uploaded to the Wordpress
    server to work out what isn't already there and needs to be uploaded
    """

    program_log.debug("Obtaining existing image URLs")
    response = requests.get(media_url, auth=oauth)
    program_log.debug("Request URL: {}".format(response.url))
    program_log.debug("Response status code: {}".format(response.status_code))
    program_log.debug("Response content: {}".format(response.text))
    images = json.loads(response.text)
    image_urls = {
        os.path.basename(x.get("guid", {}).get("rendered")):
        x.get("guid", {}).get("rendered")
        for x in images
    }
    program_log.debug("Existing images on remote: {}".format(image_urls))
    return image_urls
コード例 #4
0
def create_blank_post(url, oauth):
    """ Create a blank post on the wordpress blog. This is executed to obtain
    a post id with which images can be uploaded, and the post can be update
    with correct content
    """

    payload = {
        "date": datetime.datetime.now(),
        "status": "draft",
        "title": "placeholder",
        "content": "placeholder",
        "author": 1,
        "comment_status": "open",
        "ping_status": "closed",
        "format": "standard",
    }

    response = requests.post(url, data=payload, auth=oauth)

    program_log.debug(
        textwrap.dedent("""
        ---------------- request ----------------
        {req.method} {req.url}
        {reqhdrs}

        {req.body}
        ---------------- response ----------------
        {res.status_code} {res.reason} {res.url}
        {reshdrs}

        {res.text}

    """).format(
            req=response.request,
            res=response,
            reqhdrs=format_headers(response.request.headers),
            reshdrs=format_headers(response.headers),
        ))

    return json.loads(response.text).get("id", None)
コード例 #5
0
def get_image_replacement_map(soup, media_url, post_id, oauth):
    """Create a dictionary associating the current image file path with the URL
    to replace it with, using the filename it will be uploaded as, as the key
    """
    images_in_doc = [x["src"] for x in soup.find_all("img")]
    program_log.debug("Local image links: {}".format(images_in_doc))
    images_existing = get_existing_images(media_url, oauth)
    image_map = {}

    program_log.debug("Constructing map of local links to remote links")
    for img in images_in_doc:
        target_filename = "{}-{}".format(post_id, os.path.basename(img))
        image_map[target_filename] = {
            "local_path": img,
            "target_path": images_existing.get(target_filename, None),
        }
    program_log.debug("Extracted image map: {}".format(image_map))

    return image_map
コード例 #6
0
def publish(ctx, **kwargs):
    """Publish a markdown file to a wordpress blog
    """

    # Set logging verbosity
    if ctx.obj.get("verbosity") or ctx.obj.get("verbosity") == 0:
        # Default console verbosity will be INFO level logging
        if ctx.obj.get("verbosity") == 0:
            logger.remove_handler(program_log, logging.StreamHandler)
        else:
            logger.adjust_handler_level(program_log, logging.StreamHandler,
                                        logging.DEBUG)

    program_log.debug("Context values: {}".format(str(ctx.obj)))

    # Consolidate cli args with config file
    config = parse_args(**kwargs)

    program_log.debug("Config: {}".format(str(config)))

    oauth = OAuth1(
        client_key=config.get("client_key", None),
        client_secret=config.get("client_secret", None),
        resource_owner_key=config.get("resource_owner_key", None),
        resource_owner_secret=config.get("resource_owner_secret", None),
        signature_type="auth_header",
    )
    program_log.debug("OAuth object built")

    program_log.debug("Reading data from {}".format(config.get("file")))
    # Read markdown file contents
    with open(config.get("file"), "r") as f:
        data = f.read()

    program_log.debug("Loading metadata")
    with open(METADATA_FILE, "r") as f:
        metadata = json.loads(f.read())

    if not metadata.get("id", None):
        program_log.info("No Post ID found. Creating a new post.")
        url = construct_url(
            base_url=config.get("base_url"),
            api_version=config.get("api_version"),
            endpoint="posts",
        )
        program_log.debug(
            "Constructing URL for /posts endpoint: {}".format(url))

        # Create a blank post to obtain a post id
        metadata["id"] = create_blank_post(url, oauth)
        program_log.debug("New post id: {}".format(str(metadata.get("id"))))

        # Update the repository metadata file with new post ID. This change
        # will need to be committed back to the repository for persistence.
        program_log.debug("Updating local metadata file with new post id")
        with open(METADATA_FILE, "w") as f:
            f.write(json.dumps(metadata, indent=4))

    if metadata.get("id", None):
        program_log.info("Publishing content")

        # Transform content into a form appropriate for wordpress
        document = parse_document(data, config, metadata, oauth)
        payload = {
            "date": None,
            **document,
            **metadata,
        }
        program_log.debug("Payload: \n{}".format(payload))
        url = construct_url(
            base_url=config.get("base_url"),
            api_version=config.get("api_version"),
            endpoint="posts",
            post_id=metadata.get("id"),
        )
        program_log.debug("Constructed URL for post update: {}".format(url))

        # Manually set the header to ensure that the body of the request is not used to sign it
        headers = {"Content-Type": "application/json"}
        # Push new blog content
        response = requests.post(url,
                                 data=json.dumps(payload),
                                 headers=headers,
                                 auth=oauth)
        program_log.debug(
            textwrap.dedent("""
            ---------------- request ----------------
            {req.method} {req.url}
            {reqhdrs}

            {req.body}
            ---------------- response ----------------
            {res.status_code} {res.reason} {res.url}
            {reshdrs}

            {res.text}

        """).format(
                req=response.request,
                res=response,
                reqhdrs=format_headers(response.request.headers),
                reshdrs=format_headers(response.headers),
            ))

        # If blog successfully published
        if response.status_code == 200:
            response_dict = json.loads(response.text)

            # Update relevant metadata from response
            with open(METADATA_FILE, "w") as f:
                f.write(json.dumps(metadata, indent=4))

            program_log.info(
                "Post successfully published, and is available at {}".format(
                    response_dict.get("guid").get("rendered")))
        else:
            program_log.error(
                "Something went wrong. The server returned status code: {}: {}"
                .format(response.status_code, response.text))
            program_log.debug(
                textwrap.dedent("""
                ---------------- request ----------------
                {req.method} {req.url}
                {reqhdrs}

                {req.body}
                ---------------- response ----------------
                {res.status_code} {res.reason} {res.url}
                {reshdrs}

                {res.text}

            """).format(
                    req=response.request,
                    res=response,
                    reqhdrs=format_headers(response.request.headers),
                    reshdrs=format_headers(response.headers),
                ))
    else:
        program_log.error("Something went wrong. Content not updated")

    return response.status_code