Coder Social home page Coder Social logo

miniblog's Introduction

MiniBlog

A blogging engine based on HTML5 and ASP.NET. For an ASP.NET Core version, see Miniblog.Core.

Build status

Deploy to Azure

Live demo: http://miniblog.azurewebsites.net/
Username: demo
Password: demo

Custom theme

In search for custom designed themes for MiniBlog? Click here.

Simple, flexible and powerful

A minimal, yet full featured blog engine using ASP.NET Razor Web Pages. Perfect for the blogger who wants to selfhost a blog.

Features

  • Best-in-class performance
  • Gets a perfect score of 100/100 on Google Page Speed
  • Uses CDN for Bootstrap and jQuery in release mode (debug="false")
  • Easy setting for serving static files from another domain.
  • Open Live Writer (OLW) support
  • Optimized for OLW
  • Assumes OLW is the main way to write posts
  • You don't have to use OLW (but you should)
  • RSS and ATOM feeds
  • Schedule posts to be published on a future date
  • Get feedback on an unpublished post by sending a preview link
  • SEO optimized
  • Uses HTML 5 microdata to add semantic meaning
  • Support for robots.txt and sitemap.xml
  • Theming support
  • Based on Bootstrap themes. Makes it easy to customize your blog
  • Comes with a one-column, two-column, and off-canvas theme
  • No database required
  • Uses the same XML format as BlogEngine.NET
  • Move your existing blog to MiniBlog using MiniBlog Formatter
  • Inline editing of blog posts
  • Comments support
  • Gravatar support
  • Can easily be replaced by 3rd-party commenting system
  • Drag 'n drop images to upload
  • Automatically optimizes uploaded images
  • Uses latest technologies
  • OpenGraph enabled
  • Based on jQuery and Bootstrap
  • Best-in-class accessibility
  • Mobile friendly
  • Works on any host including Windows Azure Websites

Why another blog engine?

7 years have passed since I started the BlogEngine.NET project. It was using cutting edge technology for its time and quickly became the most popular blogging platform using ASP.NET.

The MiniBlog was born as a test to see what a modern blog engine could look like today with the latest ASP.NET and HTML 5 technologies. Just like with BlogEngine.NET, the goal was to see how small and simple such a blog engine could be.

This is the result.

Connecting with Open Live Writer (OLW)

To connect to MiniBlog with Open Live Writer:

  • Launch Open Live Writer

  • If you have not used Open Live Writer to connect to a blog you will get a dialog window asking you to specify what blog service you use. If you have already connected Open Live Writer to a blog, you can go to Blogs -> Add blog account... and get to the same dialog window.

  • In the What blog service do you use? dialog window you will tick the Other services radio option and click next.

  • The Add a blog account dialog window will ask you for the web address of your blog, the username and password. The web address is the root address of your site. For example, use http://miniblog.azurewebsites.net/ for the live demo site.

  • The Download Blog Theme dialog window will let you know Open Live Writer can download your blog theme if you allow it to publish a temporary post. Selecting yes will allow you to view how your posts will look directly from the Open Live Writer editor.

  • The Select blog type dialog window will let you know Open Live Writer was not able to detect your blog type. It will ask you for the type of blog and the remote posting URL.
    Type of blog that you are using: Metaweblog API
    Remote posting URL for your blog: http://<root-address>/metaweblog
    Click next.

  • The Your blog has been set up dialog window will let you give your blog a nickname for the Open Live Writer instance. Change that if you want and click finish to get to posting!

Open Live Writer can be downloaded at:
https://openlivewriter.com/

Configuring MiniBlog as Virtual Application

MiniBlog is very compact and can be configured as a Virtual Application so you'd be able to use it alongside your existing websites. For example if you've got a running ASP.NET website at http://yourexamplesite.com/ and you want to setup a blog under /blog/ path, you could setup http://yourexamplesite.com/blog/ with a few simple tweaks in web.config settings:

  • Set blog:path element of appSettings to the virtual path that you've configured for MiniBlog. Example with path blog
<add key="blog:path" value="blog"/>
  • Update the path attribute of all the <handlers> in web.config. Example with path blog
<handlers>
    <remove name="CommentHandler"/>
    <add name="CommentHandler" verb="*" type="CommentHandler" path="/blog/comment.ashx"/>
    <remove name="PostHandler"/>
    <add name="PostHandler" verb="POST" type="PostHandler" path="/blog/post.ashx"/>
    <remove name="MetaWebLogHandler"/>
    <add name="MetaWebLogHandler" verb="POST,GET" type="MetaWeblogHandler" path="/blog/metaweblog"/>
    <remove name="FeedHandler"/>
    <add name="FeedHandler" verb="GET" type="FeedHandler" path="/blog/feed/*"/>
    <remove name="CssHandler"/>
    <add name="CssHandler" verb="GET" type="MinifyHandler" path="/blog*.css"/>
    <remove name="JsHandler"/>
    <add name="JsHandler" verb="GET" type="MinifyHandler" path="/blog*.js"/>
</handlers>

<httpErrors>
    <remove statusCode="404"/>
    <error statusCode="404" responseMode="ExecuteURL" path="/blog/404.cshtml"/>
</httpErrors>

After changing the config all that is left is configuring a Virtual Application with the same path(ex. blog) inside your IIS website.

miniblog's People

Contributors

alexwiese avatar am11 avatar appetere avatar asapostolov avatar benmccallum avatar bradygaster avatar cdhunt avatar darrenwaller avatar dragon753 avatar dzimchuk avatar fredrikeriksson avatar gordonbeeming avatar jddonovan avatar jiveabillion avatar jonaslorander avatar jongalloway avatar jphellemons avatar jschwarty avatar jwidmer avatar lurenyao avatar maads avatar madskristensen avatar nicolastarzia avatar paulinfrancis avatar peterkneale avatar pinopino avatar programcsharp avatar sam-ward avatar simonbu11 avatar z1c0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

miniblog's Issues

Sign in link goes to wrong URL

Cloned repo (e711945), testing locally in either WebMatrix or Visual Studio 2012, the Sign In link returns a 404.

When clicked, the Sign In link goes to:

http://localhost:[portnum]/Account/Login?ReturnUrl=/

According to web.config, the login Url should be:

~/views/login/

and indeed, if I manually modify the Url to:

http://localhost:[portnum]/views/Login?ReturnUrl=/

I am then able to sign in successfully.

Found a workaround to correct the issue here:

http://stackoverflow.com/questions/4087300/asp-net-mvc-issue-with-configuration-of-forms-authentication-section

Submitted a pull request for the workaround.

Read More Functionality

Is there a way to add the "read more" functionality to the "OneColumn" & "TwoColumns" themes?

Antiforgery.Validate(cookie, token) can fail

I have run across a bug where in Blog.cs the AntiForgery.Validate(cookie.Value, token) can fail when a comment is posted. I have tried resolving the issue to no avail.

Here is how I can normally reproduce it:

  1. Open IE and Chrome and navigate to the blog
  2. In IE click on a blog post
  3. In Chrome click on the same blog post
  4. Go back to IE and post a comment
  5. Go back to Chrome, post a comment, Validation fails throwing an exception

Admin panel

Create admin page to change the blog parameters (blog name, blog image, blog description)

Caching bug when IsAuthenticated changes when not running locally.

There seems to be a bug with the caching when the logged in status changes and when the application isn't running locally.
Though the same thing also happens if the if-condition is removed in the Index.cshtml file.

The result is that the admin menu isn't shown when logging in or hidden when logging out until the user forces the cache to clear in the browser with ctrl+f5 or in some other way.

I've come to understand that the GetVaryByCustomString override is used to produce different versions of the cache based on the auth cookie value.
However it doesn't seem like it applies for some reason.

I could pinpoint the specific code that seemed to break this:

if (DateTime.TryParse(incomingDate, out testDate) && testDate == lastModified)
{
    response.ClearContent();
    response.StatusCode = (int)System.Net.HttpStatusCode.NotModified;
    response.SuppressContent = true;
}

By uncommenting this block things started to behave normally again but of course now all resources is returned with Status OK, except for the CDN resources of course.

I'm not that experienced with the entire cache-mechanism in ASP.NET so maybe someone else have some insight?

An ugly workaround for this could be to just add an additional check for if the user isn't authenticated, but this probably defeats the whole purpose of the GetVaryByCustomString in the first place.

Drag 'n Drop drop issue with self closing tag

When a zip file is dropped on web-based editor, on page reload it renders an invalid markup:

<a href="/posts/files/10676ef9-4224-4031-b1ac-c339c10a918e.zip" /></a>

The opening tag is self closing. IE shows the following error:

HTML1500: Tag cannot be self-closing. Use an explicit closing tag.
File: chrome-extension-web-developer-checklist, Line: 61, Column: 33

Also, there is no way to set the inner text of uploaded zip file, when dragged on editor.

Problems refreshing blog after update with WLW

I'm running a slightly modified version of MiniBlog on Windows Azure. When I update a post with WLW, the underlying XML file in the posts folder gets updated, but when I refresh the site (either home page or specific page for the updated blog) the cached unmodified version of the post appears. Once I log in to the site, then my change does appear.

Is this something anyone else is seeing? My understanding of ASP.NET caching is fairly limited. I would have thought that LastModified changing should have caused any cached version of the page to be invalidated.

Could it be a UTC versus Local Time thing? LastModified seems to be UTC, but it gets converted to local time before SetConditionalGetHeaders

(BTW, many thanks for creating MiniBlog, has helped me get my own blog up and running really quickly)

Files attachment and youtube/vemo videos

"The ability to upload files (zip, pdf, and other docs) and add a link to download" is missing from MiniBlog. Cool addition that would be!

Also, the YouTube and Viemo video linking can be made easy via web editor.

Thanks.

Added comment message doesn't show up in Firefox 26

The message after posting a comment doesn't show up in Firefox 26, I dig into the script files and i changed the line number 175 in comments.js to the following

elemStatus.innerHTML = "Your comment has been added";

it works fine, but I don't know if there's an impact of this minor change

Error on New Post from remote host

This is on a new instance. The only change to the site was switching to Windows authentication. New post works running debug in IIS Express and it works browsing the site from localhost. Only fails when accessing the site remotely.

Log Name:      Application
    Source:        ASP.NET 4.0.30319.0
    Date:          4/21/2014 3:30:48 PM
    Event ID:      1309
    Task Category: Web Event
    Exception information: 
    Exception type: ArgumentOutOfRangeException 
    Exception message: Specified argument was out of the range of valid values.
    Parameter name: utcDate
   at System.Web.HttpCachePolicy.UtcSetLastModified(DateTime utcDate)
     at System.Web.HttpCachePolicy.SetLastModified(DateTime date)
     at Blog.SetConditionalGetHeaders(DateTime lastModified, HttpContextBase context) in c:\inetpub\wwwroot\MiniBlog\app_code\code\Blog.cs:line 198
     at ASP._Page_Index_cshtml.Execute() in c:\inetpub\wwwroot\MiniBlog\Index.cshtml:line 59
     at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
     at System.Web.WebPages.WebPage.ExecutePageHierarchy()
     at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
     at System.Web.WebPages.WebPageHttpHandler.ProcessRequestInternal(HttpContextBase httpContext)


Thread information: 
    Thread ID: 6 
    Is impersonating: False 
    Stack trace:    at System.Web.HttpCachePolicy.UtcSetLastModified(DateTime utcDate)
     at System.Web.HttpCachePolicy.SetLastModified(DateTime date)
     at Blog.SetConditionalGetHeaders(DateTime lastModified, HttpContextBase context) in c:\inetpub\wwwroot\MiniBlog\app_code\code\Blog.cs:line 198
     at ASP._Page_Index_cshtml.Execute() in c:\inetpub\wwwroot\MiniBlog\Index.cshtml:line 59
     at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
     at System.Web.WebPages.WebPage.ExecutePageHierarchy()
     at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
     at System.Web.WebPages.WebPageHttpHandler.ProcessRequestInternal(HttpContextBase httpContext)

Compileable to pure static files

I would love the possibility to compile the blog to pure static files and host on example Github Pages or Azure BLOB Storage.

This would be possible by using for example wget:
wget -r --html-extension -nH http://localhost:56577

I wonder if we could add what is missing for that to work, for example routing with ".html" support.

Are pages planned? Question

Hi,

I'm impressed - very cool MiniBlog.
What's about a mini-cms (like dotnetBlogEngine -:)) with pages and themes ???

regards, Otto

Is it possible to promise valid XHTML in posts' XML <content> element?

I am having a hard time dealing with Content markup in C# code. The self-closing tags don't have />, for this reason, when the markup is loaded with .NET's XPathDocument, XElement, XNode XmlReader, XmlDocument and XDocument etc. all throw exception -- complain about invalid end tag -- as there is no HtmlTextReader or XhtmlTextReader in .NET (and I don't want to add third party HtmlAgilityPack in this sleekly light project to make it bulky 8-)).

Is it possible that we ensure the contained markup (from HTML5 based content editor and MiniBlog converter) in future would be a valid XHTML?

Google sitemap has issues with the date

When you add http://yourdomain.com/sitemap.xml to the google webmaster tools, the google webmaster tools give errors on each entry because of the date format. A value like this seems invalid according to google "2/18/2014 9:26:45 AM" according to the google webmaster tools it should be in format "yyyy-MM-ddTuu:mmTZD" So line 8 on /views/Robots/Sitemap.cshtml should be

<lastmod>@post.LastModified.ToString("yyyy-MM-ddThh:mmzzz")</lastmod> 

Save post doesn't work

if i try save or edit post. This is error "Something bad happened. Server reported 500 Internal Server Error"
That's all :(

Alternate theme with Zurb Foundation

Please add an alternate theme with Zurb Foundation grid system.

The idea is to have 12 columns per row.

Here is a sample grid:

 <div class="row">
      <div class="columns large-4 small-2">
         Spanning four columns for large screens, two columns in small screens.
     </div>
      <div class="columns large-6">
         Spanning four columns for both large and small screens.
     </div>
      <div class="columns large-2 small-4">
         Spanning four columns for both large, two columns for small screens .
     </div>
 </div>

<div class="row">
      <div class="columns large-2 small-4 large-centered small-centered">
         Spanning two columns for large screens and four columns for small screens. Both aligned centered.
      </div>
</div>


<div class="row">
      <div class="columns large-2 hide-for-small">
         Spanning two columns for large screens and hidden for small screens.
      </div>
</div>

Find more about grid in their docs.

Also there are classes for pagination and lot more.

Virtual Directory Support

I published MiniBlog in a virtual directory and all of the references are broken. They exclude the virtual directory from the URL creating invalid HREFs. Is there a web.config setting I can make to support the virtual directory hosted site?

Running MiniBlog in a virtual directory has issues

When I run MB in a virtual dir, mb does not have a good uri to the css and js files. and when I click on an url of a specific post I get 403.18 in iis7. When I access exactly the same version with a subdomain, all works fine (theme, js references and no 403 http code)

the blogengine slug removal does not work because the other slug rule is first

so you have to swap the order of the slug url rewrite rules in the web.config:

<rule name="BlogEngine slug" stopProcessing="true">
          <match url="^post/(.*)\.aspx" ignoreCase="true"/>
          <action type="Redirect" redirectType="Permanent" url="/post/{R:1}"/>
        </rule>
        <rule name="slug" stopProcessing="true">
          <match url="^post/(.*)" ignoreCase="true"/>
          <action type="Rewrite" url="/?slug={R:1}"/>
        </rule>

Error deploying the blog as Virtual Application in Azure Cloud Services

I'm deploying the blog as part of Cloud Services in Azure. I have RDP in the instance, and I can see that it's all OK in IIS, and I can even see the blog from IE in the instance itself. When I try to access the blog from outside the instance I get this exception:

Any ideas? Thanks!!

Server Error in '/blog' Application.

File not found
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.IO.FileNotFoundException: File not found
Source Error:
Line 179: throw new FileNotFoundException("File not found", absolute);
Line 180:
Line 181: DateTime date = File.GetLastWriteTime(absolute);
Line 182: int index = relative.LastIndexOf('.');
Line 183:

Source File: f:\sitesroot\1\app_code\code\Blog.cs Line: 181
Stack Trace:
[FileNotFoundException: File not found]
Blog.FingerPrint(String rootRelativePath, String cdnPath) in f:\sitesroot\1\app_code\code\Blog.cs:181
ASP._Page_themes_BurritoData__Layout_cshtml.Execute() in f:\sitesroot\1\themes\BurritoData_Layout.cshtml:9
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +280
System.Web.WebPages.WebPage.ExecutePageHierarchy() +339
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +125
System.Web.WebPages.WebPageBase.RenderSurrounding(String partialViewName, Action`1 body) +209
System.Web.WebPages.WebPageBase.PopContext() +199
System.Web.WebPages.WebPageHttpHandler.ProcessRequestInternal(HttpContextBase httpContext) +218

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.34009

Support Comment Approve

Hi @madskristensen ,
Very thanks for Great MiniBlog.

Please add comments approve feature which after approve comments, they can be display in public mode.

thanks in advance.

Something wrong with "~/" resolution in href attributes

repro:
Open http://miniblog.azurewebsites.net/ demo site.
Title link "miniblog" correctly points to site root "/"
Categoriy links and post links are also correct.

Now click on "crome" category in the post "http://miniblog.azurewebsites.net/category/chrome".
Now title link points to "http://miniblog.azurewebsites.net/category/" and category links are having double category in them ("http://miniblog.azurewebsites.net/category/category/chrome")
Same story if you click the post title. All "~/" Urls inherit a "/post/" part in them.

My educated guess is that it is somehow tied with UrlRewriting. I've encountered similar behavior previosly.
This only affects "~/" usage in which is parsed by razor not in other cases where helper methods are called.
Now I remember filing an issue about it
https://aspnetwebstack.codeplex.com/workitem/58

Ok, I ran "category" page through debugger and Href("/") yields "/category/"
Now in web.config we have a rewrite rule
<rule name="category" stopProcessing="true"> <match url="^category/([^/]+)(/page/)?([\d]+)?" ignoreCase="true"/> <action type="Rewrite" url="/?category={R:1}&amp;page={R:3}"/> </rule>
And dumping Request object of the same request gave us AppRelativeCurrentExecutionFilePath: "
/"

Miniblog in Subdirectory

Hello Mads,
Miniblog is good and great blog engine, i am using this blog engine and i put this blog in subdirectory instead of root named blog but I am unable to do the configuration to add new post after admin login can you please help, its urgent

Thank You.

Question: WindowsLiveWriter Support with anon access denied

I have set the following authorization nodes in the web.config, but I still cannot get WLW to register with my blog instance. Is there anything else I need to open up to anon to allow MetaWeblog protocol to function? Is that even functionally possible without granting everyone access to the content?

 <authorization>
      <deny users="?" />
    </authorization>

  <location path="themes">
    <system.web>
      <authorization>
      <allow users="*"/> 
      </authorization>
      </system.web>
  </location>

  <location path="metaweblog">
    <system.web>
      <authorization>
      <allow users="*"/> 
      </authorization>
      </system.web>
  </location>  

Using miniblog in an existing project

Dear Mads
Thanks for this great and beautifully simple software. I have a question about it if you can help me;
Is there any way for me to use this software along with my existing asp.net mvc project other than creating a new virtual folder and read the posts with rss feed?

Ajax post error

When posting a comment in Chrome 31 the following error is thrown:

Refused to set unsafe header "Connection"

According to this information on stackoverflow the error is cause by line 32 in comments.js

ajaxRequest.setRequestHeader("Connection", "close");

I'd do a pull request but I've already taken apart my version too much.

pingback and trackback

I do not know if this is still a valid feature for a blog and if it would benefit SEO. But I remember that blogengine had it. Should miniblog have that feature?

AdminMenu.cshtml WYSIWYG editor does not work

The WYSIWYG editor will not work when creating or editing a post because the admin.js script executes before the bootstrap-wysiwyg.js script and the wysiwyg widget is not available at the time the admin.js script tries to use it. Removing the async attribute from the scripts in the code below fixes the bug.

<script src="@Blog.FingerPrint("/scripts/bootstrap-wysiwyg.js")" async defer></script>
<script src="@Blog.FingerPrint("/scripts/admin.js")" async defer></script>

Regarding the use of the async attribute on script tags, it should not be used on scripts that depend on each other because any of the scripts could be loaded before the other. You can find reference to this rule in this stackoverflow article http://stackoverflow.com/questions/3952009/defer-attribute-chrome (point no. 4 in the "So what should a web developer use?" section of the selected answer).

Store Posts (XML) in Blob storage?

Hi, thanks for this great blog. I'm trying to host this as a Virtual Application in Azure Cloud Services. It's working, but when I redeploy, I'd need to download the posts from the server to not lose them. Which is not very handy if I redeploy often. Does the blog support storing the posts in a DB / Azure Blob Storage? That way I could redeploy without worrying about the content.

Thanks,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.