A PHP project that renders markdown files from GitHub repositories. Based on Parsedown
- History
- Features
- Implementation Overview
- Running the Project
- Development and Debugging
- IMPORTANT Things to Note
Initially I was looking for a way to render markdown files for a blog project I was working on. The intent at that time was to be able to render small markdown files that were to be used as blog entries.
However, later on I found that I wanted to render my GitHub README and project documentation files for use on my personal website. And the intent there was to have the ability to showcase README content without having to direct visitors to my GitHub account.
I had investegated a number of potential solutions and determined that Parsedown would work best for my applications.
The following features are enhancements to the operation of the original Parsedown -
- Extended with the
ParsedownModify
class. Added two functions -modifyVoid()
- modifies thesrc
attribute for in-line<img>
tags. For example, the images in this file are<img>
tags wrapped in<p>
tags. This is intentional, and aids in editing.modifyInline()
- modifies the GitHub flavored images :![an image](./path/to/image.jpg)
- Utilizes the
ModifyElements
class to effect changes to the image and link tags.
Here are the application features -
- Configurable - The following items are configurable via JSON formatted files which can be chosen using a query when the page is loaded into a browser.
- GitHub Access - Note that this application does not require a Personal access token.
- Can be configured to retrieve the markdown file from a GitHub repository using the following options -
- Repository Name
- Owner
- Branch
- Markdown file - The file can be local or hosted on GitHub
- Can be configured to retrieve the markdown file from a GitHub repository using the following options -
- Generates Static HTML - An optional setting controls this feature, the output file name is also configurable.
- Open Graph Meta Tags - Can be configure with meta tag contents, allows for links to the page to be Twitter Cards or other rich links for Facebook, LinkedIn, etc
- Meta Tags - Can be configured in the JSON file or can be retrieved from the GitHub repository.
- Meta Description
- Meta Keywords
- Code Coloring - Using PrismJS for creating colorized code blocks.
- GitHub Access - Note that this application does not require a Personal access token.
- Modifies resource paths for images. The configurable GitHub settings are used in on-the-fly modification of image tags so that the
src
attributes point to the correct location. - Modifies resource paths for in-line links such as
[test.md](./test.md)
and addstarget="_blank"
to the resulting HTML link. The configurable GitHub settings are used in on-the-fly modification of link tags so that thehref
attributes point to the correct location. Note : The code expects a relative path to the root of the repository.
PHP was the primary technology used in this project. It provides all of the necessary capabilities needed to access and render the markdown content to HTML.
- PHP - Version 5.6 was used in development and testing. It was chosen because standard hosting was the targeted platform.
- Web Server - This project can be hosted on a internet accessible host. However for initial use and testing a local server such as XAMPP is recommended. This project was developed and tested on XAMPP - xampp-win32-5.6.31-0-VC11-installer.exe
- Web Browser - My preferred development browser is Chrome.
I used Netbeans 8.2 for the majority of my debugging. It works very well with PHP and Chrome.
- Download this repository as a zip file.
- Unzip the contents to your hard drive.
- Create a folder in
c:\xampp\htdocs
calledtests\mdrender
. - Copy the following into
c:\xampp\htdocs\tests\mdrender
-
- Folders & contents -
nbproject
- not required if running on a hosting serverassets
mdimg
- Files -
index.php
Parsedown.php
ParsedownModify.php
RenderConfig.php
github.json
mdpageopt.json
codecolor.json
test.json
test.md
- Run XAMPP and start Apache (not necessary if running on a hosting server)
- Open your browser and navigate to -
http://[localhost | server]/tests/mdrender/index.php
- The page you see should look like this -
Here is the file in GitHub - test.md (right-click and open in a new tab or window).
github.json : Typically it will not be necessary to edit this file. It contains GitHub specific configuration items that are not likely to change often.
{
"reporaw" : "https://raw.githubusercontent.com/",
"repogit" : "https://github.com/",
"repoapi" : "https://api.github.com/",
"accheader": [
"application/vnd.github.v3+json",
"application/vnd.github.mercy-preview+json"
],
}
It should not be necessary to edit the following in the github.json
file -
reporaw
- base URL for accessing raw GitHub filesrepogit
- base URL for accessing GitHubrepoapi
- base URL for accessing the GitHub APIaccheader
- an array of twoAccept
headers, selected in code for specific API calls.
test.json : This file and its contents are specific to the Markdown file that you want to render.
{
"owner" : "jxmot",
"repo" : "markdown-render",
"branch" : "master",
"mdfilerem": true,
"mdfile" : "test.md",
"mdpageopt": "./mdpageopt.json",
"pagetitle": "markdown-render Test",
"gitdesc" : false,
"metadesc" : "",
"gittopics": false,
"metakeyw" : "test,mdrender,markdown",
"metaauth" : "https://github.com/jxmot",
"genstatic": true,
"statname" : "./test.html",
"oghead": true,
"ogjson": "./oghead-example-test.json",
"codecolor": true,
"codecolorfiles": "./codecolor.json"
}
The following found in test.json
can be edited as needed -
owner
- this is the owner of the repository where the markdown file to be rendered is residing.repo
- the repository name that contains the markdown filebranch
- the branch that contains the markdown filemdfilerem
- iftrue
the application will obtain the markdown file from the repository, if it isfalse
it will look for the file locallymdfile
- the name of the targeted markdown filemdpageopt
- the path + name of the configuration file which contains settings for page footer, social icons, and "to top" functionalitypagetitle
- this will become the text between the<title>
tags in the rendered outputgitdesc
- iftrue
the application will obtain the description from the specified repository, iffalse
it will use the text found inmetadesc
metadesc
- optional, used ifgitdesc
isfalse
gittopics
-true
the application will obtain the topics found in the repository and place them in the meta keywords as a comma separated list, iffalse
it will use the text found inmetakeyw
metakeyw
- optional, used ifgittopics
isfalse
metaauth
- optional, fills in the meta author tag if there if it has text in itgenstatic
- iftrue
the application will create a static HTML file from the rendered output.statname
- the name of the generated static HTML file, since the rendered file will use the CSS and JS files it is best to save it in the current location (i.e../
)oghead
- iftrue
then meta tags containing Open Graph protocol data will be included within the<head>
tags. NOTE :genstatic
must betrue
, otherwise this field is ignoredogjson
- the path + name of the configuration file which contains the data for the Open Graph meta tagscodecolor
- iftrue
then code block colorization is enabled.codecolorfiles
- points to a file that contains links to the necessary CSS and JS files.
Additional JSON files can be created as needed and contain different repository information. To run the application using a different JSON file is accomplished using a query. For example if a JSON file named myreadme.json
is to be used then point the browser to - http://localhost/tests/mdrender/index.php?cfg=myreadme
.
mdpageopt.json : There are additional features that are configurable via a another JSON file -
{
"footer": true,
"footertxt": " 2017 © James Motyl ",
"socicon": true,
"socitems": [
{ "url":"https://github.com/jxmot/", "class":"gh", "target":"_blank", "title":"See me on GitHub!" },
{ "url":"https://www.linkedin.com/in/jim-motyl/", "class":"in", "target":"_blank", "title":"See me on LinkedIn!" }
],
"totop": true
}
footer
- Iftrue
a fixed position footer will be added to the page and the following items can also be configured. However if it isfalse
, non existent, or if the file is missing then there will be no footer.footertxt
- text centered in the footersocicon
- iftrue
then both social icons will be seensocitems[0]
- used for the icon on the left sidesocitems[1]
- used for the icon on the right side
totop
- "Go to Top" button, a simple "go to top" button that can be reused on any web page. It is implemented with the following -assets/css/totop.css
assets/js/totop.js
- an HTML button located at the bottom of the document space -
<button id="gototop" class="gototop" onclick="jumpToTop()" title="Go to top of page">▲<br>top</button>
oghead-example-test.json : This file contains the required content for the "Open Graph" protocol. I used it on pages shared with Twitter and LinkedIn.
In test.json
-
{
"genstatic": true,
"statname" : "./test.html",
"oghead": true,
"ogjson": "./oghead-example-test.json"
}
The Opeh Graph tags will not be rendered unless genstatic
and oghead
are true. The configuration for the meta tag content is in oghead-example-test.json
. You can find the details in the oghead-example-test document.
codecolor.json : The codecolor.json
file contains the necessary CSS and JavaScript tags for using PrismJS -
{
"links":[
"<link href=\"./assets/prism/prism.css\" rel=\"stylesheet\"/>",
"END"
],
"scripts":[
"<script src=\"./assets/prism/prism.min.js\"></script>",
"END"
]
}
Individual "code coloring" JSON files can be created as needed, and document configuration files can specify their own file. This was done due to the nature of the PrismJS dowload process will provide differnt CSS/JS files depending upon selected options. If addtional CSS or JS files are needed they can be inserted into the "links"
or "scripts"
arrays before "END"
. If using the PrismJS CDN those URLs can also be located in this file.
The Open Graph options in this application are intended for use when creating a static page from the rendered ouput. Even if you want to continue live rendering of the page a static HTML would be necessary in order for the Open Graph parts to work correctly.
The Open Graph meta tags that are generated were intend for use on Facebook, LinkedIn, and Twitter. They have not been extensively tested elsewhere but are likely to work as expected.
Other things to know are -
- In the
twitter:url
andog:url
meta tags theurl
must end in/
or reference an existing file. - It seems that a larger thumbnail image works best. I've read conflicting info regarding the size of the image, and my choice for larger image is due to what I read in the Facebook Best Practices docs.
- If problems occur try using one or more of these to find errors -
- Facebook Object Debugger. You have to be logged into Facebook in order for the debugger to work.
- Twitter Card Validator. You have to be logged into Twitter in order for the debugger to work.
- Social Debug - It "grades" your meta tags, seems to work pretty well.
Sometimes there are issues when adding a link to LinkedIn's media or to posts where the image is incorrect. If that happens edit the link and place a small meaningless query at the end of the URL. This seems to force LinkedIn to read the Open Graph tags right away. An example URL - https://yoursite.com/
?1
. Each time it needs to be updated just edit the link and change the query.
The Twitter site and application do not appear to show the image right away. I think that the link target isn't scraped for the thumbnail until the post is viewed for the first time, not counting the original post. So if it doesn't show up right away quit the application or browser and restart.
The bulk of the page styling is done with Bootstrap and a CSS file(assets/css/document.css
). The coloring and some other style adjustments in that CSS file are tailored for use with the cyborg Bootstrap theme.
- Development Operating System - Windows 10 64bit
I used XAMPP and NetBeans 8.2(PHP) to develop and debug this project. In order to properly debug with the NetBeans IDE it is necessary to modify the XAMPP php.ini
file. Contrary to the majority of on-line resources the correct settings are -
[XDebug]
zend_extension = "./php_xdebug.dll"
; XAMPP and XAMPP Lite 1.7.0 and later come with a bundled xdebug at <XAMPP_HOME>/php/ext/php_xdebug.dll, without a version number.
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
; Port number must match debugger port number in NetBeans IDE Tools > Options > PHP
xdebug.remote_handler=dbgp
xdebug.profiler_enable=1
xdebug.profiler_output_dir="<XAMPP_HOME>\tmp"
Add the section above to your php.ini
file. Under XAMPP it is located at C:\xampp\php\php.ini
. In addition, this repository contains the NetBeans project settings files. They are located in /nbproject
. After you have XAMPP and NetBeans installed it should be possible to open the project in Netbeans.
- Download and install XAMPP, make the modifcations describe above to the
php.ini
file. - Download and install NetBeans, download the PHP/HTML5 flavor of NetBeans.
- Run NetBeans
- Then File->Open Project and navigate to
c:\xampp\htdocs\tests\mdrender
and open the project
- Then File->Open Project and navigate to
NetBeans will allow you to set breakpoints and examine variables.
Copy the files as described in Running the Project to a folder on your server's document root. To run the test render navigate your browser to - http[s]://yourserver/yourfolder/index.php
. The default configuration is in test.json
. To run a different JSON configuration file create one with the appropriate modifications (use test.json
as a starting point) and copy it to the folder on your server. Then you can navigate to - http[s]://yourserver/yourfolder/index.php?cfg=yourconfig
.
The version of Parsedown used in this repository is realatively old. It was created around May 2017. I estimate that the version would have been 1.6.3, however the author(s) did not update the version number string in Parsedown.php
.
There have been a large number of changes made at Parsedown since the time when the current version was first obtained. I plan on updating the local copy of Parsedown after activivity has settled down in the Parsedown repository.
UPDATE 2018-03-08 : After much (very much) tinkering around with some "updated" version of parsedown I've decided that the version I'm using now will have to do. There were some changes that severly broke what I'm trying to do. Since the original code is not commented sufficiently it became increasing difficult to determine what exactly has changed and what the intent was. The problems were evident in embedded images, for example - ![some text](path/to/image.jpg)
. The 1.7.0 and 1.7.1 versions of Parsedown treated those links and processed them as such. During that process they're converted back to an embedded image. After I studied the Parsdown code in detail it appeared to me that the reason for that was a "fudge". Which was manifested as a call to inlineImage() and then from within that function to call inlineLink(). By calling inlineLink() the internals of the element array were manipulated to look like a <a>
tag.
© 2018 James Motyl