Merge pull request #333 from justinwalsh/development

Merge development to master
This commit is contained in:
Stéphane Goetz 2015-12-11 22:34:06 +01:00
commit 31c7df153b
257 changed files with 10458 additions and 5548 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ node_modules
.DS_Store .DS_Store
/sftp-config.json /sftp-config.json
static static
vendor

View File

@ -3,4 +3,4 @@ RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !\.(gif|jpg|png|css|js|html|ico|zip|rar|pdf|xml|mp4|mpg|flv|swf|mkv|ogg|avi|woff|svg|eot|ttf|jar)$ index.php [L,QSA] RewriteRule . index.php [L,QSA]

View File

@ -2,8 +2,6 @@ module.exports = function (grunt) {
'use strict'; 'use strict';
grunt.loadNpmTasks('grunt-php'); grunt.loadNpmTasks('grunt-php');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.initConfig({ grunt.initConfig({
php: { php: {
@ -11,35 +9,12 @@ module.exports = function (grunt) {
options: { options: {
keepalive: true, keepalive: true,
open: true, open: true,
port: 8085 port: 8085,
router: "index.php"
} }
} }
},
less: {
development: {
options: {
cleancss: true,
report: 'min'
},
files: {
"css/daux-blue.min.css": "less/daux-blue.less",
"css/daux-green.min.css": "less/daux-green.less",
"css/daux-navy.min.css": "less/daux-navy.less",
"css/daux-red.min.css": "less/daux-red.less"
} }
}
},
watch: {
scripts: {
files: ['less/**/*.less'],
tasks: ['less'],
options: {
nospawn: true
},
},
},
}); });
//grunt.registerTask('default', ['less', 'watch']);
grunt.registerTask('default', ['php']); grunt.registerTask('default', ['php']);
}; };

View File

@ -35,7 +35,7 @@ Do you use Daux.io? Send me a pull request or open an [issue](https://github.com
## Download ## Download
Download this repository as a zip, and unpack. Copy the files to a web server that can run PHP 5.3 or greater. You can also run the documentation locally using Grunt.js, which is covered at the end of this readme. Download this repository as a zip, and unpack. Copy the files to a web server that can run PHP 5.4 or greater. You can also run the documentation locally using Grunt.js, which is covered at the end of this readme.
## Folders ## Folders
@ -74,7 +74,7 @@ If you want to create a beautiful landing page for your project, simply create a
{ {
"title": "Daux.io", "title": "Daux.io",
"tagline": "The Easiest Way To Document Your Project", "tagline": "The Easiest Way To Document Your Project",
"image": "<base_url>img/app.png" "image": "app.png"
} }
``` ```
@ -170,17 +170,6 @@ Include custom links in the sidebar.
} }
``` ```
###File editor:
![File editor](https://f.cloud.github.com/assets/1788727/1954191/44358884-81d1-11e3-859d-254b9fb81808.png)
Enable front-end Markdown editor. _Disabled by default_.
```json
{
"file_editor": true
}
```
###Google Analytics: ###Google Analytics:
This will embed the google analytics tracking code. This will embed the google analytics tracking code.
@ -247,6 +236,17 @@ If your server does not have a default timezone set in php.ini, it may return er
} }
``` ```
###Inherit Index
This feature will insruct the router to seek the first available file to use when a request to a folder is made and the index is not found.
```json
{
"live": [
"inherit_index": true
]
}
```
###Multi-language ###Multi-language
Enables multi-language support which needs seperate directories for each language in `docs/` folder. Enables multi-language support which needs seperate directories for each language in `docs/` folder.
@ -280,7 +280,7 @@ Directory structure:
## Running Remotely ## Running Remotely
Copy the files from the repo to a web server that can run PHP 5.3 or greater. Copy the files from the repo to a web server that can run PHP 5.4 or greater.
## Running Locally ## Running Locally

17
bin/compile Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
require __DIR__.'/../vendor/autoload.php';
use Todaymade\Daux\Compiler;
error_reporting(-1);
ini_set('display_errors', 1);
try {
$compiler = new Compiler();
$compiler->compile();
} catch (\Exception $e) {
echo 'Failed to compile phar: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine();
exit(1);
}

View File

@ -12,7 +12,21 @@
} }
], ],
"require": { "require": {
"php": ">=5.3", "php": ">=5.4",
"erusev/parsedown": "~1.0" "league/plates": "~3.1",
"guzzlehttp/guzzle": "~5.3",
"league/commonmark": "^0.11",
"symfony/console": "~2.7",
"symfony/finder": "~2.7",
"webuni/commonmark-table-extension": "0.4.*"
},
"autoload": {
"psr-4": {
"Todaymade\\Daux\\Extension\\": "daux/",
"Todaymade\\Daux\\": "libs/"
}
},
"require-dev": {
"phpunit/phpunit": "~4"
} }
} }

1444
composer.lock generated

File diff suppressed because it is too large Load Diff

BIN
daux.phar Executable file

Binary file not shown.

10
daux/Processor.php Normal file
View File

@ -0,0 +1,10 @@
<?php namespace Todaymade\Daux\Extension;
use Todaymade\Daux\Tree\Root;
class Processor extends \Todaymade\Daux\Processor {
public function manipulateTree(Root $root)
{
}
}

View File

@ -1,27 +0,0 @@
{
"title": "My Project",
"tagline": "My Stylish Documentation",
"author": "I, Me & Myself",
"image": "",
"theme": "daux-blue",
"breadcrumbs": false,
"template": "default",
"clean_urls": false,
"toggle_code": false,
"date_modified": false,
"float": false,
"file_editor": false,
"repo": "",
"twitter": [],
"google_analytics": "",
"timezone": "America/Los_Angeles",
"piwik_analytics": false,
"piwik_analytics_id": "0",
"languages": {},
"ignore": {
"files": [],
"folders": []
},
"links": {
}
}

View File

@ -16,6 +16,9 @@
* Supports Google Analytics and Piwik Analytics * Supports Google Analytics and Piwik Analytics
* Optional code float layout * Optional code float layout
* Static Output Generation * Static Output Generation
* Internal documentation links
* Multiple Output Formats
* Extend Daux.io with Processors
## Demos ## Demos
@ -27,7 +30,6 @@ This is a list of sites using Daux.io:
* [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee) * [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
* [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/) * [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
* [TrackJs](http://docs.trackjs.com) (uses a customized theme) * [TrackJs](http://docs.trackjs.com) (uses a customized theme)
* [Sugoi](http://doc.sugoi.ventrux.com/)
* [wallabag](http://doc.wallabag.org/index) * [wallabag](http://doc.wallabag.org/index)
* [Ultimo Docs](http://docs.ultimogroup.co.nz/) * [Ultimo Docs](http://docs.ultimogroup.co.nz/)
@ -35,7 +37,48 @@ Do you use Daux.io? Send me a pull request or open an [issue](https://github.com
## Download ## Download
Download this repository as a zip, and unpack. Copy the files to a web server that can run PHP 5.3 or greater. You can also run the documentation locally using Grunt.js, which is covered at the end of this readme. Download this repository as a zip, and unpack. Copy the files to a web server that can run PHP 5.3 or greater.
You can also run the documentation locally using Grunt.js, which is covered at the end of this readme.
If you don't intend to modify Daux.io and just want to use it, you only need to copy `resources`, `daux.phar`, `global.json`, `generate`, `serve` and `index.php` With these, you're ready to create your documentation.
If however you wish to do some advanced modifications, I recommend you use the raw version and run `composer install` to get started.
## Generating a set of static files
These can be uploaded to a static site hosting service such as pages.github.com
Generating a complete set of pages, with navigation
```bash
./generate --destination=[Output Directory Relative Direction]
```
For more options, run
```bash
./generate --help
```
## Formats
Daux.io is extendable and comes by default with two export formats:
- Export to HTML
- Upload to your Atlassian Confluence server
## Feature Matrix
Feature | HTML | Single Page HTML | Confluence
--------------:|:----:|:----------------:|:----------:
Multilanguage | √ | X (Soon) | X
Landing Pages | √ | X | X
Index Pages | √ | √ | √
Internal Links | √ | X (Soon) | √
Code Highlight | √ | X (Soon) | √ (Using macros)
Live Mode | √ | X | X
Pages Ordering | √ | √ | X (API Limitation)
Google / Piwik analytics | √ | √ | √ (Configured on Conflence)
## Folders ## Folders
@ -70,37 +113,38 @@ You might also wish to stick certain links to the bottom of a page. You can do s
## Landing page ## Landing page
If you want to create a beautiful landing page for your project, simply create a `_index.md` file in the root of the `/docs` folder. This file will then be used to create a landing page. You can also add a tagline and image to this page using the config file like this: If you want to create a beautiful landing page for your project, create a `_index.md` file in the root of the `/docs` folder. This file will then be used to create a landing page. You can also add a tagline and image to this page using the config file like this:
```json ```json
{ {
"title": "Daux.io", "title": "Daux.io",
"tagline": "The Easiest Way To Document Your Project", "tagline": "The Easiest Way To Document Your Project",
"image": "<base_url>img/app.png" "image": "app.png"
} }
``` ```
Note: The image can be a local or remote image. Use the convention `<base_url>` to refer to the root directory of the Daux instance. Note: The image can be a local or remote image. By default, the path is relative to the root of the documentation
## Section landing page ## Section Index page
If you are interested in having a landing page for a subsection of your docs, all you need to do is add an `index.md` file to the folder. For example, `/docs/01_Examples` has a landing page for that section since there exists a `/docs/01_Examples/index.md` file. If you wish to have an landing page for a section sans the document format, use the name `_index.md` By default, a folder will have no index page. if you wish to have one defined all you need to do is add an `index.md` file to the folder. For example, `/docs/01_Examples` has a landing page for that section since there exists a `/docs/01_Examples/index.md` file.
## Clean URLs ## Internal links
Daux provides native support for Clean URLs provided the webserver has its URL Rewrite module enabled. To enable the same, simply set the toggle in the `config.json` file in the `/docs` folder. You can create links from a page to an other, the link is then resolved to the real page.
Creating a link to another page is done exactly like a normal markdown link. In the url part, start with `!` and set the absolute path to the file, omitting the numbering and file extension
A link to `01_Examples/05_Code_Highlighting.md` Would be written like this: `[Code Highlight Examples](!Examples/Code_Highlighting)`
The page generation will fail if a link is wrong.
```json
{
"clean_urls": true
}
```
## Configuration ## Configuration
To customize the look and feel of your documentation, you can create a `config.json` file in the of the `/docs` folder. The `config.json` file is a simple JSON object that you can use to change some of the basic settings of the documentation. To customize the look and feel of your documentation, you can create a `config.json` file in the of the `/docs` folder. The `config.json` file is a JSON object that you can use to change some of the basic settings of the documentation.
###Title: ### Title
Change the title bar in the docs Change the title bar in the docs
```json ```json
@ -109,117 +153,25 @@ Change the title bar in the docs
} }
``` ```
###Themes: ### Tagline
We have 4 built-in Bootstrap themes. To use one of the themes, just set the `theme` option to one of the following: Change the tagline bar in the docs
* daux-blue
* daux-green
* daux-navy
* daux-red
```json ```json
{ {
"theme": "daux-blue" "tagline": "The Easiest Way To Document Your Project"
} }
``` ```
###Custom Theme: ### Author
To use a custom theme, just copy over the theme folder as well as the `.thm` file for that theme into the `themes` directory and set its value in the `theme` param in config.json Change the documentation's author
```json ```json
{ {
"theme": "new-theme", "author": "Stéphane Goetz"
} }
``` ```
###Code Floating: ### Ignore
By default your code blocks will be floated to a column on the right side of your content. To disable this feature, set the `float` property to `false`.
```json
{
"float": false
}
```
###Toggling Code Blocks
Some users might wish to hide the code blocks & view just the documentation. By setting the `toggle_code` property to `true`, you can offer a toggle button on the page.
```json
{
"toggle_code": true
}
```
###GitHub Repo:
Add a 'Fork me on GitHub' ribbon.
```json
{
"repo": "justinwalsh/daux.io"
}
```
###Twitter:
Include twitter follow buttons in the sidebar.
```json
{
"twitter": ["justin_walsh", "todaymade"]
}
```
###Links:
Include custom links in the sidebar.
```json
{
"links": {
"GitHub Repo": "https://github.com/justinwalsh/daux.io",
"Help/Support/Bugs": "https://github.com/justinwalsh/daux.io/issues",
"Made by Todaymade": "http://todaymade.com"
}
}
```
###File editor:
![File editor](https://f.cloud.github.com/assets/1788727/1954191/44358884-81d1-11e3-859d-254b9fb81808.png)
Enable front-end Markdown editor. _Disabled by default_.
```json
{
"file_editor": true
}
```
###Google Analytics:
This will embed the google analytics tracking code.
```json
{
"google_analytics": "UA-XXXXXXXXX-XX"
}
```
###Piwik Analytics:
This will embed the piwik tracking code.
```json
{
"piwik_analytics": "my-url-for-piwik.com"
}
```
You can Also give a specific Piwik ID as well.
```json
{
"piwik_analytics_id": "43"
}
```
###Ignore:
Set custom files and entire folders to ignore within your `/docs` folder. For files make sure to include the file extension in the name. For both files and folders, names are case-sensitive. Set custom files and entire folders to ignore within your `/docs` folder. For files make sure to include the file extension in the name. For both files and folders, names are case-sensitive.
```json ```json
@ -231,25 +183,6 @@ Set custom files and entire folders to ignore within your `/docs` folder. For fi
} }
``` ```
###Breadcrumb titles
Daux.io provides the option to present page titles as breadcrumb navigation. You can *optionally* specify the separator used for breadcrumbs.
```json
{
"breadcrumbs": true,
"breadcrumb_separator" : " > "
}
```
###Date Modified
By default, daux.io will display the last modified time as reported by the system underneath the title for each document. To disable this, change the option in your config.json to false.
```json
{
"date_modified": false
}
```
### Timezone ### Timezone
If your server does not have a default timezone set in php.ini, it may return errors when it tries to generate the last modified date/time for docs. To fix these errors, specify a timezone in your config file. Valid options are available in the [PHP Manual](http://php.net/manual/en/timezones.php). If your server does not have a default timezone set in php.ini, it may return errors when it tries to generate the last modified date/time for docs. To fix these errors, specify a timezone in your config file. Valid options are available in the [PHP Manual](http://php.net/manual/en/timezones.php).
@ -290,43 +223,271 @@ Directory structure:
│ │ │ ├── 05_Code_Highlighting.md │ │ │ ├── 05_Code_Highlighting.md
``` ```
## Running Remotely ### Format
Change the output format. It is recommended you set only formats that support the live mode as this will also
be read by the integrated web server. And you set the other formats (like confluence) only by command line
```json
{
"format": "html"
}
```
### Processor
You can set the processor in the documentation or as an option to the command line. If you need it when running the server, you should add it to the configuration.
More information on how to create a Processor can be found [here](!For_Developers/Creating_a_Processor).
```json
{
"processor": "MyProcessor"
}
```
### HTML Export Configuration
#### Themes
We have 4 built-in Bootstrap themes. To use one of the themes, just set the `theme` option to one of the following:
* daux-blue
* daux-green
* daux-navy
* daux-red
```json
{
"html": { "theme": "daux-blue" }
}
```
#### Custom Theme
To use a custom theme, just copy over the theme folder into the `themes` directory and set its value in the `theme` param in config.json
```json
{
"html": { "theme": "new-theme" }
}
```
#### Code Floating
By default your code blocks will be floated to a column on the right side of your content. To disable this feature, set the `float` property to `false`.
```json
{
"html": { "float": false }
}
```
#### Toggling Code Blocks
Some users might wish to hide the code blocks & view just the documentation. By setting the `toggle_code` property to `true`, you can offer a toggle button on the page.
```json
{
"html": { "toggle_code": true }
}
```
#### GitHub Repo
Add a 'Fork me on GitHub' ribbon.
```json
{
"html": { "repo": "justinwalsh/daux.io" }
}
```
#### Twitter
Include twitter follow buttons in the sidebar.
```json
{
"html": { "twitter": ["justin_walsh", "todaymade"] }
}
```
#### Links
Include custom links in the sidebar.
```json
{
"html": {
"links": {
"GitHub Repo": "https://github.com/justinwalsh/daux.io",
"Help/Support/Bugs": "https://github.com/justinwalsh/daux.io/issues",
"Made by Todaymade": "http://todaymade.com"
}
}
}
```
#### Google Analytics
This will embed the google analytics tracking code.
```json
{
"html": { "google_analytics": "UA-XXXXXXXXX-XX" }
}
```
#### Piwik Analytics
This will embed the piwik tracking code.
```json
{
"html": { "piwik_analytics": "my-url-for-piwik.com" }
}
```
You can Also give a specific Piwik ID as well.
```json
{
"html": { "piwik_analytics_id": "43" }
}
```
#### Breadcrumb titles
Daux.io provides the option to present page titles as breadcrumb navigation. You can *optionally* specify the separator used for breadcrumbs.
```json
{
"html": {
"breadcrumbs": true,
"breadcrumb_separator" : " > "
}
}
```
#### Date Modified
By default, daux.io will display the last modified time as reported by the system underneath the title for each document. To disable this, change the option in your config.json to false.
```json
{
"html": { "date_modified": false }
}
```
### Confluence Upload Configuration
#### Configuring the connection
The connection requires three parameters `base_url`, `user` and `pass`. While `user` and `pass` don't really need an explanation, for `base_url` you need to set the path to the server without `rest/api`, this will be added automatically.
```json
{
"confluence": {
"base_url": "http://my_confluence_server.com/,
"user" : "my_username",
"pass" : "my_password",
}
}
```
#### Where to upload
Now that the connection is defined, you need to tell it where you want your documentation to be uploaded.
For that you need a `space_id` (name that appears at the beginning of the urls) and an `ancestor_id`; the id of the page that will be the parent of the documentation's homepage.
You can obtain the `ancestor_id` id by editing the page you want to define as a parent: the ID is at the end of the URL
```json
{
"confluence": {
"space_id": "my_space",
"ancestor_id": 50370632
}
}
```
#### Prefix
Because confluence can't have two pages with the same name in a space, I recommend you define a prefix for your pages.
```json
{
"confluence": { "prefix": "[DAUX]" }
}
```
## Live mode
Keep in mind, this mode can be used for production, but it is not recommended.
The whole directory must be scanned on each request. This might not make a big impact on small documentations but can be a bottleneck on bigger ones.
### Running Locally
There are several ways to run the docs locally. You can use something like <a href="http://www.mamp.info/en/index.html" target="_blank">MAMP</a> or <a href="http://www.wampserver.com/en/" target="_blank">WAMP</a>.
The easiest is to use PHP 5.4's built-in server.
For that i've included a short command, run `./serve` in the projects folder to start the local web server. By default the server will run at: <a href="http://localhost:8085" target="_blank">http://localhost:8085</a>
### Running Remotely
#### Clean URLs configuration
Daux provides native support for Clean URLs provided the webserver has its URL Rewrite module enabled.
To enable the same, set the toggle in the `config.json` file in the `/docs` folder.
```json
{
"live": {
"clean_urls": true
}
}
```
#### Apache
Copy the files from the repo to a web server that can run PHP 5.3 or greater. Copy the files from the repo to a web server that can run PHP 5.3 or greater.
## Running Locally There is an included `.htaccess` for Apache web server.
There are several ways to run the docs locally. You can use something like <a href="http://www.mamp.info/en/index.html" target="_blank">MAMP</a> or <a href="http://www.wampserver.com/en/" target="_blank">WAMP</a>. If you are like me and use alot of Node.js and <a href="http://gruntjs.com/" target="_blank">Grunt.js</a>, then you can use the optional grunt command I have packaged with the project which will start a PHP web server for you in the project folder. #### Nginx
The Grunt.js task uses the built in web server in PHP 5.4 to host the docs on your local machine. This is really only intended be used when you are writing/updating a ton of docs and want to preview the changes locally. Daux.io works perfectly fine on Nginx too, just drop this configuration in your `nginx.conf`
**To use the optional Grunt command you will need:** ```
server {
listen 8085;
server_name localhost;
* Node.js index index.html index.php;
* npm charset utf-8;
* Grunt.js
* PHP 5.4 or greater (This is because of the built-in web server packaged in 5.4)
This project contains a package.json file, so once you have the requirements installed, you can simply run a `npm install` and then `grunt` in the projects folder to start the local web server. By default the server will run at: <a href="http://localhost:8085" target="_blank">http://localhost:8085</a> root /var/www/docs;
## Generating a set of static files location / {
if (!-e $request_filename){
rewrite ^(.*)$ /index.php$1;
}
}
These can be uploaded to a static site hosting service such as pages.github.com location ~ \.php {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
Generating a complete set of pages, with navigation fastcgi_pass unix:/var/run/php5-fpm.sock;
#fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
```bash include fastcgi_params;
php generate.php [global.json Relative Location] [Output Directory Relative Direction] fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
``` ```
## Running on IIS ### IIS
If you have set up a local or remote IIS web site, you may need a `web.config` with: If you have set up a local or remote IIS web site, you may need a `web.config` with:
* A rewrite configuration, for handling clean urls. * A rewrite configuration, for handling clean urls.
* A mime type handler for less files, if using a custom theme. * A mime type handler for less files, if using a custom theme.
### Clean URLs #### Clean URLs
The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`: The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`:
@ -351,19 +512,10 @@ The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`:
To use clean URLs on IIS 6, you will need to use a custom URL rewrite module, such as [URL Rewriter](http://urlrewriter.net/). To use clean URLs on IIS 6, you will need to use a custom URL rewrite module, such as [URL Rewriter](http://urlrewriter.net/).
### Less Mime Type ## Known Issues
The `web.config` needs a new `<mimeMap>` entry, under `<staticContent>` in `<system.webServer>`: - __Windows UTF-8 files support__ Files with UTF-8 characters cannot be handled on windows, this issue has no known fix yet.
```xml
<staticContent>
<mimeMap fileExtension=".less" mimeType="text/css" />
</staticContent>
```
You will only need the mime map entry if you are using a custom theme and receive 404s for `.less` files.
If you have a global mime map entry for `.less` files set for the server, you will receive an internal server (500) error for having duplicate mime map entries.
## Support ## Support

View File

@ -1,38 +1,3 @@
Let's get the whole "linebreak" thing out of the way. The next paragraph contains two phrases separated by a single newline character:
Roses are red
Violets are blue
The next paragraph has the same phrases, but now they are separated by two spaces and a newline character:
Roses are red
Violets are blue
Oh, and one thing I cannot stand is the mangling of words with multiple underscores in them like perform_complicated_task or do_this_and_do_that_and_another_thing.
A bit of the GitHub spice
-------------------------
In addition to the changes in the previous section, certain references are auto-linked:
* SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* \#Num: #1
* User/#Num: mojombo#1
* User/Project#Num: mojombo/god#1
These are dangerous goodies though, and we need to make sure email addresses don't get mangled:
My email addy is tom@github.com.
Math is hard, let's go shopping
-------------------------------
In first grade I learned that 5 > 3 and 2 < 7. Maybe some arrows. 1 -> 2 -> 3. 9 <- 8 <- 7.
Triangles man! a^2 + b^2 = c^2
We all like making lists We all like making lists
------------------------ ------------------------
@ -124,17 +89,10 @@ Or perhaps someone a little less eloquent:
Table for two Table for two
------------- -------------
<table> ID | Name | Rank
<tr> ---|:------:|------:
<th>ID</th><th>Name</th><th>Rank</th> 1 | Tom Preston-Werner | Awesome
</tr> 2 | Albert Einstein | Nearly as awesome
<tr>
<td>1</td><td>Tom Preston-Werner</td><td>Awesome</td>
</tr>
<tr>
<td>2</td><td>Albert Einstein</td><td>Nearly as awesome</td>
</tr>
</table>
Crazy linking action Crazy linking action
-------------------- --------------------

View File

@ -1,4 +1,7 @@
Highlight.js highlights syntax in code examples on blogs, forums and in fact on any web pages. It's very easy to use because it works automatically: finds blocks of code, detects a language, highlights it. [Learn more.](http://softwaremaniacs.org/soft/highlight/en/) Highlight.js highlights syntax in code examples on blogs, forums and in fact on any web pages. It's very easy to use because it works automatically: finds blocks of code, detects a language, highlights it. [Learn more.](https://highlightjs.org/)
You can even use [Github Flavored Markdown](!Examples/GitHub_Flavored_Markdown)
**Python** **Python**
@ -628,38 +631,6 @@ Highlight.js highlights syntax in code examples on blogs, forums and in fact on
} }
} }
**VBScript**
' creating configuration storage and initializing with default values
Set cfg = CreateObject("Scripting.Dictionary")
' reading ini file
for i = 0 to ubound(ini_strings)
s = trim(ini_strings(i))
' skipping empty strings and comments
if mid(s, 1, 1) <> "#" and len(s) > 0 then
' obtaining key and value
parts = split(s, "=", -1, 1)
if ubound(parts)+1 = 2 then
parts(0) = trim(parts(0))
parts(1) = trim(parts(1))
' reading configuration and filenames
select case lcase(parts(0))
case "uncompressed""_postfix" cfg.item("uncompressed""_postfix") = parts(1)
case "f"
options = split(parts(1), "|", -1, 1)
if ubound(options)+1 = 2 then
' 0: filename, 1: options
ff.add trim(options(0)), trim(options(1))
end if
end select
end if
end if
next
**VB.NET** **VB.NET**
Import System Import System
@ -2121,42 +2092,3 @@ Highlight.js highlights syntax in code examples on blogs, forums and in fact on
by A3,TARSKI:def 1,TREES_1:def 2; by A3,TARSKI:def 1,TREES_1:def 2;
hence thesis by A4; hence thesis by A4;
end; end;
**Special tests**
Explicit Python highlighting
for x in [1, 2, 3]:
count(x)
Language set on <pre>
for x in [1, 2, 3]:
count(x)
HTML5-style language class (language-python)
for x in [1, 2, 3]:
count(x)
Replacing TAB with 4 spaces
for x in [1, 2, 3]:
count(x)
Custom markup
<<a href="http://dev.w3.org/html5/spec/Overview.html#the-div-element">div</a> id="contents">
<del><p>Hello, World!</del><!-- A comment should not break merging --><ins>Goodbye, cruel world!</ins>
</div>
Custom markup + TAB replacement
for x in [1, 2, 3]:
<span style="background:yellow"> </span>count(x)
if x == 3:
<span style="background:yellow"> </span>count(x + 1)

View File

@ -0,0 +1,5 @@
title: Hallo Welt
date: 12th December 1984
-------
This is a page which has attributes and a overriden Title

View File

@ -0,0 +1 @@
We also support UTF-8 file names

View File

@ -0,0 +1 @@
Even more UTF-8 file names !

View File

@ -0,0 +1,89 @@
The recommended way to extend Daux is through Processors.
The main advantage, is that you can run it with the source or with `daux.phar` independently. You don't need to hack in the core.
## Adding classes
At the same level as your `daux.phar` file, you will see a `daux` directory, you can create all your classes here.
The classes must respect the PSR-4 Naming convention. And have `\Todaymade\Daux\Extension` as a base namespace.
By default, we created a `daux/Processor.php` file to get you started.
## A quick test ?
For the example we're just going to dump the tree and exit.
```php
public function manipulateTree(Root $root)
{
print_r($root->dump());
exit;
}
```
also, add this at the beginning of the file:
```php
use Todaymade\Daux\Tree\Root;
```
Let's just try if it works by running `./generate --processor=Processor`
Yes, you get a big array dump! You're good to go.
## What can I achieve ?
There are a few methods that you can override to add some
### Change the parsed tree.
By default, Daux.io parses your directory to find pages. but, for a reason or another, you might want to programmatically add some pages.
This can be done with:
```php
public function manipulateTree(Root $root)
{
}
```
Two helpers from the class `Todaymade\Daux\Tree\Builder` will greatly help you doing that:
```php
$new = Builder::getOrCreateDir($root, 'New Pages');
$page = Builder::getOrCreatePage($new, 'index');
$page->setContent('The index page for the new folder');
$page = Builder::getOrCreatePage($new, 'A New Hope');
$page->setContent('A long time ago in a galaxy far away');
```
Both methods `getOrCreateDir` and `getOrCreatePage` take two parameters : `parent` and `title`
### Extend the Markdown Generator
You can extend the Markdown Parser in any way wou want with this method.
```php
public function extendCommonMarkEnvironment(Environment $environment)
{
}
```
See the details on [CommonMark's website](http://commonmark.thephpleague.com/customization/overview/).
### Add new generators
You can add new generators to Daux.io and use them right away, they must implement the
`\Todaymade\Daux\Format\Base\Generator` interface and if you want to use the live mode with your generator
you have to implement `\Todaymade\Daux\Format\Base\LiveGenerator`.
```php
public function addGenerators()
{
return ['custom_generator' => '\Todaymade\Daux\Extension\MyNewGenerator'];
}
```

View File

@ -6,7 +6,7 @@
<h3>Features</h3> <h3>Features</h3>
<hr/> <hr/>
<img src="img/app-thumbs.png" alt="alt text" class="img-responsive pull-right" style="margin-bottom:20px;"> <img src="app-thumbs.png" alt="alt text" class="img-responsive pull-right" style="margin-bottom:20px;">
* 100% Mobile Responsive * 100% Mobile Responsive
* Supports GitHub Flavored Markdown * Supports GitHub Flavored Markdown
@ -19,8 +19,12 @@
* Built On Bootstrap * Built On Bootstrap
* No Build Step * No Build Step
* Git/SVN Friendly * Git/SVN Friendly
* Google Analytics * Supports Google Analytics and Piwik Analytics
* Optional code float layout * Optional code float layout
* Static Output Generation
* Internal documentation links
* Multiple Output Formats
* Extend Daux.io with Processors
<div class="clear"></div> <div class="clear"></div>
<hr/> <hr/>

View File

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 235 KiB

View File

@ -2,21 +2,24 @@
"title": "DAUX.IO", "title": "DAUX.IO",
"tagline": "The Easiest Way To Document Your Project", "tagline": "The Easiest Way To Document Your Project",
"author": "Justin Walsh", "author": "Justin Walsh",
"image": "<base_url>img/app.png", "image": "app.png",
"theme": "daux-blue",
"template": "default",
"clean_urls": true,
"toggle_code": true,
"breadcrumbs": true,
"breadcrumb_separator": "Chevrons",
"date_modified": true,
"float": true,
"file_editor": false,
"repo": "justinwalsh/daux.io",
"ignore": { "ignore": {
"files": ["Work_In_Progress.md"], "files": ["Work_In_Progress.md"],
"folders": ["99_Not_Ready"] "folders": ["99_Not_Ready"]
}, },
"live": {
"inherit_index": true,
"clean_urls": true
},
"html": {
"theme": "daux-blue",
"breadcrumbs": true,
"breadcrumb_separator": "Chevrons",
"toggle_code": true,
"date_modified": true,
"float": true,
"repo": "justinwalsh/daux.io",
"twitter": ["justin_walsh", "todaymade"], "twitter": ["justin_walsh", "todaymade"],
"google_analytics": "UA-12653604-10", "google_analytics": "UA-12653604-10",
"links": { "links": {
@ -26,3 +29,4 @@
"Made by Todaymade": "http://todaymade.com" "Made by Todaymade": "http://todaymade.com"
} }
} }
}

14
generate.php → generate Normal file → Executable file
View File

@ -1,6 +1,5 @@
#!/usr/bin/env php
<?php <?php
require_once("libs/daux.php");
/* /*
Daux.io Daux.io
@ -64,9 +63,8 @@ negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage. software, even if advised of the possibility of such damage.
*/ */
if (isset($argv[1])) $Daux = new \Todaymade\Daux\Daux($argv[1]);
else $Daux = new \Todaymade\Daux\Daux(); require_once("vendor/autoload.php");
$Daux->initialize();
if (isset($argv[2])) $Daux->generate_static($argv[2]); $application = new \Todaymade\Daux\Console\Application();
else $Daux->generate_static(); $application->run();
?>

View File

@ -1,4 +1,46 @@
{ {
"docs_directory": "docs", "docs_directory": "docs",
"valid_markdown_extensions": ["md", "markdown"] "themes_directory": "themes",
"title": "My Project",
"tagline": "My Stylish Documentation",
"author": "I, Me & Myself",
"image": "",
"languages": {},
"format": "html",
"processor": "",
"ignore": {
"files": [],
"folders": []
},
"timezone": "America/Los_Angeles",
"live": {
"inherit_index": false,
"clean_urls": false
},
"html": {
"theme": "daux-blue",
"breadcrumbs": false,
"toggle_code": false,
"date_modified": false,
"float": false,
"repo": "",
"twitter": [],
"links": {
},
"google_analytics": false,
"piwik_analytics": false,
"piwik_analytics_id": "0"
},
"confluence": {
"prefix": ""
}
} }

169
gulpfile.js Normal file
View File

@ -0,0 +1,169 @@
var gulp = require('gulp'),
php = require('gulp-connect-php'),
less = require('gulp-less'),
rename = require('gulp-rename'),
plumber = require('gulp-plumber'),
postcss = require('gulp-postcss');
var resources = {
daux:{source: "themes/daux/less/theme.less", dest: "themes/daux/css/"},
daux_blue:{source: "themes/daux/less/theme-blue.less", dest: "themes/daux/css/"},
daux_green:{source: "themes/daux/less/theme-green.less", dest: "themes/daux/css/"},
daux_navy:{source: "themes/daux/less/theme-navy.less", dest: "themes/daux/css/"},
daux_red:{source: "themes/daux/less/theme-red.less", dest: "themes/daux/css/"},
daux_singlepage:{source: "themes/daux_singlepage/less/main.less", dest: "themes/daux_singlepage/css/"}
};
var unusedRules = [
//We only use one glyphicon ...
".glyphicon-",
"!.glyphicon-chevron-right",
//we dont need all buttons
".btn-",
"!.btn-primary",
"!.btn-secondary",
"!.btn-hero",
"!.btn-sidebar",
".caret",
//Typography
".h1",
".h2",
".h3",
".h4",
".h5",
".h6",
".small",
// We need only small columns
".col-",
"!.col-sm",
// We don't use a lot of navs and navbars
".navbar-fixed",
".navbar-inverse",
".navbar-default",
".nav-pills",
".nav-tabs",
".nav-stacked",
".nav-justified",
// And a few others we don't use
".bg-",
".table"
];
function prepare_rules(rules) {
var regexes = {inclusion: [], exclusion: []}, rule, pattern, regex, exclusion;
for (rule in rules) {
if (!rules.hasOwnProperty(rule)) continue;
pattern = rules[rule];
exclusion = pattern.indexOf('!') === 0;
if (exclusion) { pattern = pattern.slice(1); }
regex = pattern.replace('.', '\\.').replace('*', '(.*)');
if (exclusion) {
regexes.exclusion.push(new RegExp(regex));
} else {
regexes.inclusion.push(new RegExp(regex));
}
}
return regexes;
}
function processPatterns(patterns, string) {
var i;
for (i in patterns.exclusion) {
if (!patterns.exclusion.hasOwnProperty(i)) continue;
if (string.match(patterns.exclusion[i])) return false;
}
for (i in patterns.inclusion) {
if (!patterns.inclusion.hasOwnProperty(i)) continue;
if (string.match(patterns.inclusion[i])) return true;
}
}
function removeUnusedRules(rules) {
var regexes = prepare_rules(rules);
return function(css) {
css.eachRule(function (rule) {
var removedSome = false,
selectors = rule.selectors,
i;
for (i = 0; i < selectors.length; i++) {
if (processPatterns(regexes, selectors[i])) {
selectors.splice(i, 1);
i--;
removedSome = true;
}
}
if(removedSome) {
if (selectors.length == 0) {
rule.removeSelf();
} else {
rule.selectors = selectors;
}
}
});
return css;
}
}
function createTask(source, dest) {
return function() {
return gulp.src(source)
.pipe(less())
.pipe(postcss([
removeUnusedRules(unusedRules),
require('csswring')({
preserveHacks: true
})
]))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(dest));
}
}
var style_tasks = [];
for (var style in resources) {
gulp.task('style_' + style, createTask(resources[style].source, resources[style].dest));
style_tasks.push('style_' + style);
}
gulp.task("styles", style_tasks);
gulp.task('watch', function() {
// Watch .less files
gulp.watch('themes/daux/less/**/*.less', ['styles']);
});
gulp.task('php', function() {
php.server({
keepalive: true,
open: true,
port: 8085,
router: "index.php"
});
});
gulp.task('default', ['php']);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,6 +1,4 @@
<?php <?php
require_once("libs/daux.php");
/* /*
Daux.io Daux.io
@ -64,8 +62,26 @@ negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage. software, even if advised of the possibility of such damage.
*/ */
$Daux = new \Todaymade\Daux\Daux();
$Daux->initialize(); if (php_sapi_name() === 'cli-server') {
$page = $Daux->handle_request($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], $_REQUEST); // This file allows us to emulate Apache's "mod_rewrite"
$page->display(); // functionality from the built-in PHP web server.
?> $uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
if ($uri !== '/' && file_exists(__DIR__ . $uri)) {
return false;
}
// When the built in server is used
// the script name is the file called
$_SERVER['SCRIPT_NAME'] = '/index.php';
}
if (file_exists('vendor/autoload.php')) {
require_once('vendor/autoload.php');
} elseif (file_exists('daux.phar')) {
define('PHAR_DIR', __DIR__);
require_once("phar://" . __DIR__ . "/daux.phar/vendor/autoload.php");
} else {
throw new Exception("Impossible to load Daux, missing vendor and phar");
}
\Todaymade\Daux\Server\Server::serve($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], $_REQUEST);

6
js/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,68 +0,0 @@
$(function () {
$('.aj-nav').click(function (e) {
e.preventDefault();
$(this).parent().siblings().find('ul').slideUp();
$(this).next().slideToggle();
});
$('table').addClass('table');
$('#menu-spinner-button').click(function () {
$('#sub-nav-collapse').slideToggle();
});
$(window).resize(function () {
// Remove transition inline style on large screens
if ($(window).width() >= 768)
$('#sub-nav-collapse').removeAttr('style');
});
});
//Fix GitHub Ribbon overlapping Scrollbar
var t = $('#github-ribbon');
var a = $('article');
if (t[0] && a[0] && a[0].scrollHeight > $('.right-column').height()) t[0].style.right = '16px';
function setCodeBlockStyle(x) {
switch (x) {
default:
case 0:
toggleCodeBlockBtn.innerHTML = "Show Code Blocks Inline";
codeBlockView.addClass('float-view');
codeBlocks.removeClass('hidden');
break;
case 1:
toggleCodeBlockBtn.innerHTML = "Hide Code Blocks";
codeBlockView.removeClass('float-view');
codeBlocks.removeClass('hidden');
break;
case 2:
toggleCodeBlockBtn.innerHTML = "Show Code Blocks";
codeBlockView.removeClass('float-view');
codeBlocks.addClass('hidden');
break;
}
}
function toggleCodeBlocks() {
codeBlockState = (codeBlockState + 1) % 3;
localStorage.setItem("codeBlockState", codeBlockState);
setCodeBlockStyle(codeBlockState);
}
//Initialize CodeBlock Visibility Settings
$(function () {
toggleCodeBlockBtn = $('#toggleCodeBlockBtn')[0];
codeBlockView = $('.right-column');
codeBlocks = $('.content-page article > pre');
codeBlockState = localStorage.getItem("codeBlockState");
if (!codeBlockState) {
codeBlockState = 0;
localStorage.setItem("codeBlockState", codeBlockState);
} else codeBlockState = parseInt(codeBlockState);
if (!codeBlockView.size()) return;
if (!codeBlocks.size()) {
codeBlockState = 2;
toggleCodeBlockBtn.classList.add('hidden');
}
setCodeBlockStyle(codeBlockState);
});

View File

@ -1,53 +0,0 @@
$(document).ready(function() {
var markdown_editor = $("#markdown_editor"),
save_editor = $(".save_editor"),
editor = $(".editor");
$("#editThis").click(function() {
editor.css({"display":"block"});
markdown_editor.autosize().val();
});
$(".closeEditor").click(function() {
editor.css({"display":"none"});
});
save_editor.click(function() {
var original_text = save_editor.text();
save_editor.text("Saving...").addClass("disabled");
$.post(window.location.href, {markdown: markdown_editor.val(), method: 'DauxEdit' }, function() {
save_editor.text("Done! Reloading page in 5 seconds. You can cancel it with ESC key");
var timeout = setTimeout(function() {
location.reload()
}, 5000); // lie
$(document).keyup(function(e) {
if (e.keyCode == 27) { // esc key
clearTimeout(timeout);
save_editor.text("Page reload cancelled");
setTimeout(function() {
save_editor.text(original_text).removeClass('disabled');
}, 2000);
}
});
}).fail(function() {
save_editor.removeClass('disabled').addClass("btn-danger").text("Failed :( - try checking your read/write permissions");
setTimeout(function() {
save_editor.text(original_text).removeClass('btn-danger');
},5000);
});
});
});
/*!
Autosize v1.18.4 - 2014-01-11
Automatically adjust textarea height based on user input.
(c) 2014 Jack Moore - http://www.jacklmoore.com/autosize
license: http://www.opensource.org/licenses/mit-license.php
*/
!function(a){var b,c={className:"autosizejs",append:"",callback:!1,resizeDelay:10,placeholder:!0},d='<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',e=["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent"],f=a(d).data("autosize",!0)[0];f.style.lineHeight="99px","99px"===a(f).css("lineHeight")&&e.push("lineHeight"),f.style.lineHeight="",a.fn.autosize=function(d){return this.length?(d=a.extend({},c,d||{}),f.parentNode!==document.body&&a(document.body).append(f),this.each(function(){function c(){var b,c=window.getComputedStyle?window.getComputedStyle(m,null):!1;c?(b=m.getBoundingClientRect().width,0===b&&(b=parseInt(c.width,10)),a.each(["paddingLeft","paddingRight","borderLeftWidth","borderRightWidth"],function(a,d){b-=parseInt(c[d],10)})):b=Math.max(n.width(),0),f.style.width=b+"px"}function g(){var g={};if(b=m,f.className=d.className,j=parseInt(n.css("maxHeight"),10),a.each(e,function(a,b){g[b]=n.css(b)}),a(f).css(g),c(),window.chrome){var h=m.style.width;m.style.width="0px";{m.offsetWidth}m.style.width=h}}function h(){var e,h;b!==m?g():c(),f.value=!m.value&&d.placeholder?(a(m).attr("placeholder")||"")+d.append:m.value+d.append,f.style.overflowY=m.style.overflowY,h=parseInt(m.style.height,10),f.scrollTop=0,f.scrollTop=9e4,e=f.scrollTop,j&&e>j?(m.style.overflowY="scroll",e=j):(m.style.overflowY="hidden",k>e&&(e=k)),e+=o,h!==e&&(m.style.height=e+"px",p&&d.callback.call(m,m))}function i(){clearTimeout(l),l=setTimeout(function(){var a=n.width();a!==r&&(r=a,h())},parseInt(d.resizeDelay,10))}var j,k,l,m=this,n=a(m),o=0,p=a.isFunction(d.callback),q={height:m.style.height,overflow:m.style.overflow,overflowY:m.style.overflowY,wordWrap:m.style.wordWrap,resize:m.style.resize},r=n.width();n.data("autosize")||(n.data("autosize",!0),("border-box"===n.css("box-sizing")||"border-box"===n.css("-moz-box-sizing")||"border-box"===n.css("-webkit-box-sizing"))&&(o=n.outerHeight()-n.height()),k=Math.max(parseInt(n.css("minHeight"),10)-o||0,n.height()),n.css({overflow:"hidden",overflowY:"hidden",wordWrap:"break-word",resize:"none"===n.css("resize")||"vertical"===n.css("resize")?"none":"horizontal"}),"onpropertychange"in m?"oninput"in m?n.on("input.autosize keyup.autosize",h):n.on("propertychange.autosize",function(){"value"===event.propertyName&&h()}):n.on("input.autosize",h),d.resizeDelay!==!1&&a(window).on("resize.autosize",i),n.on("autosize.resize",h),n.on("autosize.resizeIncludeStyle",function(){b=null,h()}),n.on("autosize.destroy",function(){b=null,clearTimeout(l),a(window).off("resize",i),n.off("autosize").off(".autosize").css(q).removeData("autosize")}),h())})):this}}(window.jQuery||window.$);

1
js/highlight.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
//Daux.io Blue
@sidebar-background: #f7f7f7;
@sidebar-hover: #c5c5cb;
@lines: #e7e7e9;
@dark: #3f4657;
@light: #82becd;
@text: #2d2d2d;
@syntax-string: #022e99;
@syntax-comment: #84989b;
@syntax-number: #2f9b92;
@syntax-label: #840d7a;
@import "import/daux-base.less";

View File

@ -1,13 +0,0 @@
//Daux.io Green
@sidebar-background: #f5f5f6;
@sidebar-hover: #a0d55d;
@lines: #e7e7e9;
@dark: #000000;
@light: #8acc37;
@text: #2d2d2d;
@syntax-string: #e0ff00;
@syntax-comment: #c4e598;
@syntax-number: #097c4e;
@syntax-label: #022e99;
@import "import/daux-base.less";

View File

@ -1,13 +0,0 @@
//Daux.io Navy
@sidebar-hover: #c5c5cb;
@lines: #e7e7e9;
@sidebar-background: #f5f5f6;
@dark: #13132a;
@light: #7795b4;
@text: #2d2d2d;
@syntax-string: #000000;
@syntax-comment: #505050;
@syntax-number: #09559b;
@syntax-label: #001775;
@import "import/daux-base.less";

View File

@ -1,13 +0,0 @@
//Daux.io Red
@sidebar-hover: #eeeeee;
@lines: #eeeeee;
@sidebar-background: #f7f7f7;
@dark: #c64641; //#df4f49;
@light: #ecb5a1;
@text: #2d2d2d;
@syntax-string: #557aa2;
@syntax-comment: #ecdfd0;
@syntax-number: #9b2f7d;
@syntax-label: #a31621;
@import "import/daux-base.less";

View File

@ -1,56 +0,0 @@
// Media objects
// Source: http://stubbornella.org/content/?p=497
// --------------------------------------------------
// Common styles
// -------------------------
// Clear the floats
.media,
.media-body {
overflow: hidden;
zoom: 1;
}
// Proper spacing between instances of .media
.media,
.media .media {
margin-top: 15px;
}
.media:first-child {
margin-top: 0;
}
// For images and videos, set to block
.media-object {
display: block;
}
// Reset margins on headings for tighter default spacing
.media-heading {
margin: 0 0 5px;
}
// Media image alignment
// -------------------------
.media {
> .pull-left {
margin-right: 10px;
}
> .pull-right {
margin-left: 10px;
}
}
// Media list variation
// -------------------------
// Undo default ul/ol styles
.media-list {
padding-left: 0;
list-style: none;
}

View File

@ -1,101 +0,0 @@
//
// Basic print styles
// --------------------------------------------------
// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css
@media print {
* {
text-shadow: none !important;
color: #000 !important; // Black prints faster: h5bp.com/s
background: transparent !important;
box-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
// Don't show links for images, or javascript/internal links
a[href^="javascript:"]:after,
a[href^="#"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group; // h5bp.com/t
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
// Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245
// Once fixed, we can just straight up remove this.
select {
background: #fff !important;
}
// Bootstrap components
.navbar {
display: none;
}
.table {
td,
th {
background-color: #fff !important;
}
}
.btn,
.dropup > .btn {
> .caret {
border-top-color: #000 !important;
}
}
.label {
border: 1px solid #000;
}
.table {
border-collapse: collapse !important;
}
.table-bordered {
th,
td {
border: 1px solid #ddd !important;
}
}
}

View File

@ -1,35 +0,0 @@
/* ===========================================================================================
Markdown editor
============================================================================================== */
.editor {
position: absolute;
top: 0;
left: 0;
right: 0;
background: white;
padding: 20px;
padding-bottom: 100px;
min-height: 100%;
height: auto;
display: none;
h3 {
width: 100% !important;
}
&.paddingTop {
padding-top: 50px;
}
}
#markdown_editor {
width: 85%;
margin: 0 auto;
padding: 10px;
height: auto;
font-size: 16px;
min-height: 100px;
font-family: "Ubuntu Mono", "Consolas", monospace;
display: block;
}

175
libs/Compiler.php Normal file
View File

@ -0,0 +1,175 @@
<?php
/*
* This class is inspired from Composer's compiler
* @see https://github.com/composer/composer/blob/master/src/Composer/Compiler.php
*/
namespace Todaymade\Daux;
use Symfony\Component\Finder\Finder;
/**
* The Compiler class compiles daux into a phar
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Stéphane Goetz <stephane.goetz@onigoetz.ch>
*/
class Compiler
{
/**
* Compiles composer into a single phar file
*
* @throws \RuntimeException
* @param string $pharFile The full path to the file to create
*/
public function compile($pharFile = 'daux.phar')
{
if (file_exists($pharFile)) {
unlink($pharFile);
}
$phar = new \Phar($pharFile, 0, 'daux.phar');
$phar->setSignatureAlgorithm(\Phar::SHA1);
$phar->startBuffering();
// Daux
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->notName('Compiler.php')
->in(__DIR__ . '/../templates')
->in(__DIR__);
foreach ($finder as $file) {
$this->addFile($phar, $file);
}
// Composer libraries
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->exclude('Tests')
->in(__DIR__ . '/../vendor/symfony/console')
->in(__DIR__ . '/../vendor/guzzlehttp/guzzle/src/')
->in(__DIR__ . '/../vendor/guzzlehttp/ringphp/src/')
->in(__DIR__ . '/../vendor/guzzlehttp/streams/src/')
->in(__DIR__ . '/../vendor/league/commonmark/src/')
->in(__DIR__ . '/../vendor/league/plates/src/')
->in(__DIR__ . '/../vendor/react/promise/src/')
->in(__DIR__ . '/../vendor/webuni/commonmark-table-extension/src/');
foreach ($finder as $file) {
$this->addFile($phar, $file);
}
// Composer autoload
$this->addComposer($phar);
$this->addBinary($phar);
// Stubs
$phar->setStub($this->getStub());
$phar->stopBuffering();
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../LICENSE'), false);
unset($phar);
}
private function addFile($phar, $file, $strip = true)
{
$path = strtr(str_replace(dirname(__DIR__) . DIRECTORY_SEPARATOR, '', $file->getRealPath()), '\\', '/');
$content = file_get_contents($file);
if ($strip) {
$content = $this->stripWhitespace($content);
} elseif ('LICENSE' === basename($file)) {
$content = "\n" . $content . "\n";
}
$phar->addFromString($path, $content);
}
private function addComposer($phar)
{
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../vendor/autoload.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../vendor/composer/autoload_classmap.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../vendor/composer/autoload_files.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../vendor/composer/autoload_namespaces.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../vendor/composer/autoload_real.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../vendor/composer/ClassLoader.php'));
$content = file_get_contents(__DIR__ . '/../vendor/composer/autoload_psr4.php');
$content = str_replace('$baseDir . \'/daux\'', 'PHAR_DIR . \'/daux\'', $content);
$phar->addFromString('vendor/composer/autoload_psr4.php', $content);
}
private function addBinary($phar)
{
$content = file_get_contents(__DIR__ . '/../generate');
$content = preg_replace('{^#!/usr/bin/env php\s*}', '', $content);
$phar->addFromString('generate', $content);
}
/**
* Removes whitespace from a PHP source string while preserving line numbers.
*
* @param string $source A PHP string
* @return string The PHP string with the whitespace removed
*/
private function stripWhitespace($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= str_repeat("\n", substr_count($token[1], "\n"));
} elseif (T_WHITESPACE === $token[0]) {
// reduce wide spaces
$whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
// normalize newlines to \n
$whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
// trim leading spaces
$whitespace = preg_replace('{\n +}', "\n", $whitespace);
$output .= $whitespace;
} else {
$output .= $token[1];
}
}
return $output;
}
private function getStub()
{
return <<<'EOF'
#!/usr/bin/env php
<?php
/*
* This file is part of Daux.
*
* (c) Stéphane Goetz <onigoetz@onigoetz.ch>
*
* For the full copyright and license information, please view
* the license that is located at the bottom of this file.
*/
define('PHAR_DIR', dirname(__FILE__));
Phar::mapPhar('daux.phar');
require 'phar://daux.phar/generate';
__HALT_COMPILER();
EOF;
}
}

49
libs/Config.php Normal file
View File

@ -0,0 +1,49 @@
<?php namespace Todaymade\Daux;
use ArrayObject;
class Config extends ArrayObject
{
/**
* Merge an array into the object
*
* @param array $newValues
* @param bool $override
*/
public function merge($newValues, $override = true)
{
foreach ($newValues as $key => $value) {
// If the key doesn't exist yet,
// we can simply set it.
if (!array_key_exists($key, $this)) {
$this[$key] = $value;
continue;
}
// We already know this value exists
// so if we're in conservative mode
// we can skip this key
if ($override === false) {
continue;
}
// Merge the values only if
// both values are arrays
if (is_array($this[$key]) && is_array($value)) {
$this[$key] = array_replace_recursive($this[$key], $value);
} else {
$this[$key] = $value;
}
}
}
/**
* Merge an array into the object, ignore already added keys.
*
* @param $newValues
*/
public function conservativeMerge($newValues)
{
$this->merge($newValues, false);
}
}

View File

@ -0,0 +1,49 @@
<?php namespace Todaymade\Daux\Console;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Input\InputInterface;
class Application extends SymfonyApplication
{
/**
* Gets the name of the command based on input.
*
* @param InputInterface $input The input interface
*
* @return string The command name
*/
protected function getCommandName(InputInterface $input)
{
// This should return the name of your command.
return 'generate';
}
/**
* Gets the default commands that should always be available.
*
* @return array An array of default Command instances
*/
protected function getDefaultCommands()
{
// Keep the core default commands to have the HelpCommand
// which is used when using the --help option
$defaultCommands = parent::getDefaultCommands();
$defaultCommands[] = new Generate();
return $defaultCommands;
}
/**
* Overridden so that the application doesn't expect the command
* name to be the first argument.
*/
public function getDefinition()
{
$inputDefinition = parent::getDefinition();
// clear out the normal first argument, which is the command name
$inputDefinition->setArguments();
return $inputDefinition;
}
}

75
libs/Console/Generate.php Normal file
View File

@ -0,0 +1,75 @@
<?php namespace Todaymade\Daux\Console;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Daux;
class Generate extends SymfonyCommand
{
protected function configure()
{
$description = 'Destination folder, relative to the working directory';
$this
->setName('generate')
->setDescription('Generate documentation')
->addOption('configuration', 'c', InputArgument::OPTIONAL, 'Configuration file')
->addOption('format', 'f', InputArgument::OPTIONAL, 'Output format, html or confluence', 'html')
->addOption('processor', 'p', InputArgument::OPTIONAL, 'Manipulations on the tree')
->addOption('source', 's', InputArgument::OPTIONAL, 'Where to take the documentation from')
->addOption('destination', 'd', InputArgument::OPTIONAL, $description, 'static');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$daux = $this->prepareDaux($input);
$width = $this->getApplication()->getTerminalDimensions()[0];
// Instiantiate the processor if one is defined
$this->prepareProcessor($daux, $input, $output, $width);
// Generate the tree
$daux->generateTree();
// Generate the documentation
$daux->getGenerator()->generateAll($input, $output, $width);
}
protected function prepareDaux(InputInterface $input)
{
$daux = new Daux(Daux::STATIC_MODE);
// Set the format if requested
if ($input->getOption('format')) {
$daux->getParams()['format'] = $input->getOption('format');
}
// Set the source directory
if ($input->getOption('source')) {
$daux->getParams()['docs_directory'] = $input->getOption('source');
}
$daux->setDocumentationPath($daux->getParams()['docs_directory']);
$daux->setThemesPath($daux->getParams()['themes_directory']);
$daux->initializeConfiguration($input->getOption('configuration'));
return $daux;
}
protected function prepareProcessor(Daux $daux, InputInterface $input, OutputInterface $output, $width)
{
if ($input->getOption('processor')) {
$daux->getParams()['processor'] = $input->getOption('processor');
}
$class = $daux->getProcessorClass();
if (!empty($class)) {
$daux->setProcessor(new $class($daux, $output, $width));
}
}
}

View File

@ -0,0 +1,25 @@
<?php namespace Todaymade\Daux\Console;
use Symfony\Component\Console\Output\OutputInterface;
trait RunAction
{
protected function runAction($title, OutputInterface $output, $width, \Closure $closure)
{
$output->write($title);
$length = function_exists("mb_strlen")? mb_strlen($title) : strlen($title);
// 8 is the length of the label + 2 let it breathe
$padding = $width - $length - 10;
try {
$response = $closure();
} catch (\Exception $e) {
$output->writeln(str_pad(" ", $padding) . "[ <fg=red>FAIL</fg=red> ]");
throw $e;
}
$output->writeln(str_pad(" ", $padding) . "[ <fg=green>OK</fg=green> ]");
return $response;
}
}

View File

@ -0,0 +1,23 @@
<?php namespace Todaymade\Daux\ContentTypes;
use Todaymade\Daux\Config;
use Todaymade\Daux\Tree\Content;
interface ContentType
{
public function __construct(Config $config);
/**
* Get the file extensions supported by this Content Type
*
* @return string[]
*/
public function getExtensions();
/**
* @param string $raw The raw text to render
* @param Content $node The original node we are converting
* @return string The generated output
*/
public function convert($raw, Content $node);
}

View File

@ -0,0 +1,54 @@
<?php namespace Todaymade\Daux\ContentTypes;
use Todaymade\Daux\Tree\Content;
class ContentTypeHandler
{
/**
* @var ContentType[] $types
*/
protected $types;
/**
* @param ContentType[] $types
*/
public function __construct($types)
{
$this->types = array_reverse($types);
}
/**
* Get all valid content file extensions
*
* @return string[]
*/
public function getContentExtensions()
{
$extensions = [];
foreach ($this->types as $type) {
$extensions = array_merge($extensions, $type->getExtensions());
}
return array_unique($extensions);
}
/**
* Get the ContentType able to handle this node
*
* @param Content $node
* @return ContentType
*/
public function getType(Content $node)
{
$path = $node->getPath() ?: $node->getName();
$extension = pathinfo($path, PATHINFO_EXTENSION);
foreach ($this->types as $type) {
if (in_array($extension, $type->getExtensions())) {
return $type;
}
}
throw new \RuntimeException("no contentType found for $path");
}
}

View File

@ -0,0 +1,40 @@
<?php namespace Todaymade\Daux\ContentTypes\Markdown;
use League\CommonMark\DocParser;
use League\CommonMark\Environment;
use League\CommonMark\HtmlRenderer;
use Webuni\CommonMark\TableExtension\TableExtension;
class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter
{
/**
* Create a new commonmark converter instance.
*
* @param array $config
*/
public function __construct(array $config = array())
{
$environment = Environment::createCommonMarkEnvironment();
$environment->mergeConfig($config);
$environment->addExtension(new TableExtension());
$this->extendEnvironment($environment);
if (array_key_exists('processor_instance', $config['daux'])) {
$config['daux']['processor_instance']->extendCommonMarkEnvironment($environment);
}
$this->docParser = new DocParser($environment);
$this->htmlRenderer = new HtmlRenderer($environment);
}
protected function getLinkRenderer(Environment $environment)
{
return new LinkRenderer($environment->getConfig('daux'));
}
protected function extendEnvironment(Environment $environment)
{
$environment->addInlineRenderer('Link', $this->getLinkRenderer($environment));
}
}

View File

@ -0,0 +1,32 @@
<?php namespace Todaymade\Daux\ContentTypes\Markdown;
use Todaymade\Daux\Config;
use Todaymade\Daux\Tree\Content;
class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
{
/** @var Config */
protected $config;
/** @var CommonMarkConverter */
protected $converter;
public function __construct(Config $config)
{
$this->config = $config;
$this->converter = new CommonMarkConverter(['daux' => $config]);
}
/**
* @return array
*/
public function getExtensions()
{
return ['md', 'markdown'];
}
public function convert($raw, Content $node)
{
return $this->converter->convertToHtml($raw);
}
}

View File

@ -0,0 +1,74 @@
<?php namespace Todaymade\Daux\ContentTypes\Markdown;
use League\CommonMark\HtmlElement;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Link;
use Todaymade\Daux\Config;
use Todaymade\Daux\DauxHelper;
use Todaymade\Daux\Exception;
use Todaymade\Daux\Tree\Entry;
class LinkRenderer extends \League\CommonMark\Inline\Renderer\LinkRenderer
{
/**
* @var Config
*/
protected $daux;
public function __construct($daux)
{
$this->daux = $daux;
}
/**
* @param string $url
* @return Entry
* @throws Exception
*/
protected function resolveInternalFile($url)
{
$file = DauxHelper::getFile($this->daux['tree'], $url);
if ($file) {
return $file;
}
$file = DauxHelper::getFile($this->daux['tree'], $url . '.html');
if ($file) {
return $file;
}
throw new Exception("Could not locate file '$url'");
}
/**
* @param Link $inline
* @param ElementRendererInterface $htmlRenderer
*
* @return HtmlElement
*/
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
{
// This can't be in the method type as
// the method is an abstract and should
// have the same interface
if (!$inline instanceof Link) {
throw new \RuntimeException(
"Wrong type passed to " . __CLASS__ . "::" . __METHOD__ .
" the expected type was 'League\\CommonMark\\Inline\\Element\\Link' but '" .
get_class($inline) . "' was provided"
);
}
$element = parent::render($inline, $htmlRenderer);
$url = $inline->getUrl();
if (!empty($url) && $url[0] == '!') {
$file = $this->resolveInternalFile(ltrim($url, "!"));
$element->setAttribute('href', $this->daux['base_url'] . $file->getUrl());
}
return $element;
}
}

372
libs/Daux.php Normal file
View File

@ -0,0 +1,372 @@
<?php namespace Todaymade\Daux;
use Symfony\Component\Console\Output\NullOutput;
use Todaymade\Daux\ContentTypes\ContentTypeHandler;
use Todaymade\Daux\Tree\Builder;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
use Todaymade\Daux\Tree\Root;
class Daux
{
const STATIC_MODE = 'DAUX_STATIC';
const LIVE_MODE = 'DAUX_LIVE';
/** @var string */
public $local_base;
/** @var string */
public $internal_base;
/** @var \Todaymade\Daux\Format\Base\Generator */
protected $generator;
/** @var ContentTypeHandler */
protected $typeHandler;
/**
* @var string[]
*/
protected $validExtensions;
/** @var string */
private $docs_path;
/** @var string */
private $themes_path;
/** @var Processor */
protected $processor;
/** @var Tree\Root */
public $tree;
/** @var Config */
public $options;
/** @var string */
private $mode;
/** @var bool */
private $merged_defaults = false;
/** @var bool */
private $merged_tree = false;
/**
* @param string $mode
*/
public function __construct($mode)
{
$this->mode = $mode;
$this->local_base = $this->internal_base = dirname(__DIR__);
// In case we're inside the phar archive
// we save the path to the directory
// in which it is contained
if (defined('PHAR_DIR')) {
$this->local_base = PHAR_DIR;
}
// global.json
$this->loadBaseConfiguration();
}
/**
* @param string $override_file
* @throws Exception
*/
public function initializeConfiguration($override_file = 'config.json')
{
// Read documentation overrides
$this->loadConfiguration($this->docs_path . DIRECTORY_SEPARATOR . 'config.json');
// Read command line overrides
if (!is_null($override_file)) {
$this->loadConfiguration($this->local_base . DIRECTORY_SEPARATOR . $override_file);
}
// Set a valid default timezone
if (isset($this->options['timezone'])) {
date_default_timezone_set($this->options['timezone']);
} elseif (!ini_get('date.timezone')) {
date_default_timezone_set('GMT');
}
}
public function setThemesPath($path)
{
$this->themes_path = $path;
if (!is_dir($this->themes_path) &&
!is_dir($this->themes_path = $this->local_base . DIRECTORY_SEPARATOR . $this->themes_path)
) {
throw new Exception('The Themes directory does not exist. Check the path again : ' . $this->themes_path);
}
$this->options['themes_path'] = $this->themes_path;
$this->options['templates'] = 'templates';
}
public function setDocumentationPath($path)
{
$this->docs_path = $path;
if (!is_dir($this->docs_path) &&
!is_dir($this->docs_path = $this->local_base . DIRECTORY_SEPARATOR . $this->docs_path)
) {
throw new Exception('The Docs directory does not exist. Check the path again : ' . $this->docs_path);
}
$this->options['docs_path'] = $this->docs_path;
}
/**
* Load and validate the global configuration
*
* @throws Exception
*/
protected function loadBaseConfiguration()
{
$this->options = new Config();
// Set the default configuration
$this->options->merge([
'docs_directory' => 'docs',
'valid_content_extensions' => ['md', 'markdown']
]);
// Load the global configuration
$this->loadConfiguration($this->local_base . DIRECTORY_SEPARATOR . 'global.json', false);
}
/**
* @param string $config_file
* @param bool $optional
* @throws Exception
*/
protected function loadConfiguration($config_file, $optional = true)
{
if (!file_exists($config_file)) {
if ($optional) {
return;
}
throw new Exception('The configuration file is missing. Check path : ' . $config_file);
}
$config = json_decode(file_get_contents($config_file), true);
if (!isset($config)) {
throw new Exception('The configuration file "' . $config_file . '" is corrupt. Is your JSON well-formed ?');
}
$this->options->merge($config);
}
/**
* Generate the tree that will be used
*/
public function generateTree()
{
$this->options['valid_content_extensions'] = $this->getContentExtensions();
$this->tree = new Root($this->getParams(), $this->docs_path);
Builder::build($this->tree, $this->options['ignore']);
if (!empty($this->options['languages'])) {
foreach ($this->options['languages'] as $key => $node) {
$this->tree->getEntries()[$key]->setTitle($node);
}
}
// Enhance the tree with processors
$this->getProcessor()->manipulateTree($this->tree);
// Sort the tree one last time before it is finalized
$this->sortTree($this->tree);
$this->finalizeTree($this->tree);
}
public function sortTree(Directory $current)
{
$current->sort();
foreach ($current->getEntries() as $entry) {
if ($entry instanceof Directory) {
$this->sortTree($entry);
}
}
}
public function finalizeTree(Directory $current, $prev = null)
{
foreach ($current->getEntries() as $entry) {
if ($entry instanceof Directory) {
$prev = $this->finalizeTree($entry, $prev);
} elseif ($entry instanceof Content) {
if ($prev) {
$prev->setNext($entry);
$entry->setPrevious($prev);
}
$prev = $entry;
}
}
return $prev;
}
/**
* @return Config
*/
public function getParams()
{
if (!$this->merged_defaults) {
$default = [
//Features
'multilanguage' => !empty($this->options['languages']),
//Paths and tree
'mode' => $this->mode,
'local_base' => $this->local_base,
'docs_path' => $this->docs_path,
'themes_path' => $this->themes_path,
'templates' => 'templates'
];
$this->options->conservativeMerge($default);
$this->options['index_key'] = 'index.html';
$this->options['base_page'] = $this->options['base_url'] = '';
$this->merged_defaults = true;
}
if ($this->tree && !$this->merged_tree) {
$this->options['tree'] = $this->tree;
$this->options['index'] = $this->tree->getIndexPage() ?: $this->tree->getFirstPage();
if ($this->options['multilanguage']) {
foreach ($this->options['languages'] as $key => $name) {
$this->options['entry_page'][$key] = $this->tree->getEntries()[$key]->getFirstPage();
}
} else {
$this->options['entry_page'] = $this->tree->getFirstPage();
}
$this->merged_tree = true;
}
return $this->options;
}
/**
* @return Processor
*/
public function getProcessor()
{
if (!$this->processor) {
$this->processor = new Processor($this, new NullOutput(), 0);
}
return $this->processor;
}
/**
* @param Processor $processor
*/
public function setProcessor(Processor $processor)
{
$this->processor = $processor;
// This is not the cleanest but it's
// the best i've found to use the
// processor in very remote places
$this->options['processor_instance'] = $processor;
}
public function getGenerators()
{
$default = [
'confluence' => '\Todaymade\Daux\Format\Confluence\Generator',
'html-file' => '\Todaymade\Daux\Format\HTMLFile\Generator',
'html' => '\Todaymade\Daux\Format\HTML\Generator',
];
$extended = $this->getProcessor()->addGenerators();
return array_replace($default, $extended);
}
public function getProcessorClass()
{
$processor = $this->getParams()['processor'];
if (empty($processor)) {
return null;
}
$class = "\\Todaymade\\Daux\\Extension\\" . $processor;
if (!class_exists($class)) {
throw new \RuntimeException("Class '$class' not found. We cannot use it as a Processor");
}
//TODO :: check that it implements processor
return $class;
}
/**
* @return \Todaymade\Daux\Format\Base\Generator
*/
public function getGenerator()
{
if ($this->generator) {
return $this->generator;
}
$generators = $this->getGenerators();
$format = $this->getParams()['format'];
if (!array_key_exists($format, $generators)) {
throw new \RuntimeException("The format '$format' doesn't exist, did you forget to set your processor ?");
}
$class = $generators[$format];
if (!class_exists($class)) {
throw new \RuntimeException("Class '$class' not found. We cannot use it as a Generator");
}
$interface = 'Todaymade\Daux\Format\Base\Generator';
if (!in_array('Todaymade\Daux\Format\Base\Generator', class_implements($class))) {
throw new \RuntimeException("The class '$class' does not implement the '$interface' interface");
}
return $this->generator = new $class($this);
}
public function getContentTypeHandler()
{
if ($this->typeHandler) {
return $this->typeHandler;
}
$base_types = $this->getGenerator()->getContentTypes();
$extended = $this->getProcessor()->addContentType();
$types = array_merge($base_types, $extended);
return $this->typeHandler = new ContentTypeHandler($types);
}
/**
* Get all content file extensions
*
* @return string[]
*/
public function getContentExtensions()
{
if ($this->validExtensions) {
return $this->validExtensions;
}
return $this->validExtensions = $this->getContentTypeHandler()->getContentExtensions();
}
}

354
libs/DauxHelper.php Normal file
View File

@ -0,0 +1,354 @@
<?php namespace Todaymade\Daux;
use Todaymade\Daux\Tree\Directory;
class DauxHelper
{
/**
* Set a new base_url for the configuration
*
* @param Config $config
* @param string $base_url
*/
public static function rebaseConfiguration(Config $config, $base_url)
{
// Avoid changing the url if it is already correct
if ($config['base_url'] == $base_url && !empty($config['theme'])) {
return;
}
// Change base url for all links on the pages
$config['base_url'] = $config['base_page'] = $base_url;
$config['theme'] = static::getTheme($config, $base_url);
$config['image'] = str_replace('<base_url>', $base_url, $config['image']);
}
public static function resolveVariant(Config $params)
{
if (array_key_exists('theme-variant', $params['html'])) {
return;
}
if (is_dir($params['themes_path'] . DIRECTORY_SEPARATOR . $params['html']['theme'])) {
return;
}
$theme = explode('-', $params['html']['theme']);
$params['html']['theme-variant'] = array_pop($theme);
$params['html']['theme'] = implode('-', $theme);
if (!is_dir($params['themes_path'] . DIRECTORY_SEPARATOR . $params['html']['theme'])) {
throw new \RuntimeException("Theme '{$params['html']['theme']}' not found");
}
}
/**
* @param Config $params
* @param string $current_url
* @return array
*/
public static function getTheme(Config $params, $current_url)
{
self::resolveVariant($params);
$theme_folder = $params['themes_path'] . DIRECTORY_SEPARATOR . $params['html']['theme'];
$theme_url = $params['base_url'] . $params['themes_directory'] . '/' . $params['html']['theme'] . '/';
$theme = array();
if (is_file($theme_folder . DIRECTORY_SEPARATOR . "config.json")) {
$theme = json_decode(file_get_contents($theme_folder . DIRECTORY_SEPARATOR . "config.json"), true);
if (!$theme) {
$theme = array();
}
}
//Default parameters for theme
$theme += [
'name' => $params['html']['theme'],
'css' => [],
'js' => [],
'fonts' => [],
'favicon' => '<base_url>themes/daux/img/favicon.png',
'templates' => $theme_folder . DIRECTORY_SEPARATOR . 'templates',
'variants' => [],
];
if (array_key_exists('theme-variant', $params['html'])) {
$variant = $params['html']['theme-variant'];
if (!array_key_exists($variant, $theme['variants'])) {
throw new Exception("Variant '$variant' not found for theme '$theme[name]'");
}
// These will be replaced
foreach (['templates', 'favicon'] as $element) {
if (array_key_exists($element, $theme['variants'][$variant])) {
$theme[$element] = $theme['variants'][$variant][$element];
}
}
// These will be merged
foreach (['css', 'js', 'fonts'] as $element) {
if (array_key_exists($element, $theme['variants'][$variant])) {
$theme[$element] = array_merge($theme[$element], $theme['variants'][$variant][$element]);
}
}
}
$substitutions = [
'<local_base>' => $params['local_base'],
'<base_url>' => $current_url,
'<theme_url>' => $theme_url
];
// Substitute some placeholders
$theme['templates'] = strtr($theme['templates'], $substitutions);
$theme['favicon'] = utf8_encode(strtr($theme['favicon'], $substitutions));
foreach (['css', 'js', 'fonts'] as $element) {
foreach ($theme[$element] as $key => $value) {
$theme[$element][$key] = utf8_encode(strtr($value, $substitutions));
}
}
return $theme;
}
/**
* @param string $path
* @return string
*/
public static function getCleanPath($path)
{
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array();
foreach ($parts as $part) {
if ('.' == $part) {
continue;
}
if ('..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
return implode(DIRECTORY_SEPARATOR, $absolutes);
}
/**
* Locate a file in the tree. Returns the file if found or false
*
* @param Directory $tree
* @param string $request
* @return Tree\Content|Tree\Raw|false
*/
public static function getFile($tree, $request)
{
$request = explode('/', $request);
foreach ($request as $node) {
// If the element we're in currently is not a
// directory, we failed to find the requested file
if (!$tree instanceof Directory) {
return false;
}
// if the node exists in the current request tree,
// change the $tree variable to reference the new
// node and proceed to the next url part
if (isset($tree->getEntries()[$node])) {
$tree = $tree->getEntries()[$node];
continue;
}
// At this stage, we're in a directory, but no
// sub-item matches, so the current node must
// be an index page or we failed
if ($node !== 'index' && $node !== 'index.html') {
return false;
}
return $tree->getIndexPage();
}
// If the entry we found is not a directory, we're done
if (!$tree instanceof Directory) {
return $tree;
}
if ($index = $tree->getIndexPage()) {
return $index;
}
return false;
}
/**
* Generate a URL friendly "slug" from a given string.
*
* Taken from Stringy
*
* @param string $title
* @return string
*/
public static function slug($title)
{
foreach (static::charsArray() as $key => $value) {
$title = str_replace($value, $key, $title);
}
$title = preg_replace('/[^\x20-\x7E]/u', '', $title);
$separator = '_';
// Convert all dashes into underscores
$title = preg_replace('![' . preg_quote("-") . ']+!u', $separator, $title);
// Remove all characters that are not the separator, letters, numbers, or whitespace.
$title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', $title);
// Replace all separator characters and whitespace by a single separator
$title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
return trim($title, $separator);
}
/**
* Returns the replacements for the slug() method.
*
* Taken from Stringy
*
* @return array An array of replacements.
*/
public static function charsArray()
{
static $charsArray;
if (isset($charsArray)) {
return $charsArray;
}
return $charsArray = array(
'a' => array(
'à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ',
'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ä', 'ā', 'ą',
'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ',
'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ',
'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ'),
'b' => array('б', 'β', 'Ъ', 'Ь', 'ب'),
'c' => array('ç', 'ć', 'č', 'ĉ', 'ċ'),
'd' => array('ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ',
'д', 'δ', 'د', 'ض'),
'e' => array('é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ',
'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ',
'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э',
'є', 'ə'),
'f' => array('ф', 'φ', 'ف'),
'g' => array('ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ج'),
'h' => array('ĥ', 'ħ', 'η', 'ή', 'ح', 'ه'),
'i' => array('í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į',
'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ',
'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ',
'ῗ', 'і', 'ї', 'и'),
'j' => array('ĵ', 'ј', 'Ј'),
'k' => array('ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك'),
'l' => array('ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل'),
'm' => array('м', 'μ', 'م'),
'n' => array('ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن'),
'o' => array('ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ',
'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő',
'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό',
'ö', 'о', 'و', 'θ'),
'p' => array('п', 'π'),
'r' => array('ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر'),
's' => array('ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص'),
't' => array('ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط'),
'u' => array('ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ',
'ự', 'ü', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у'),
'v' => array('в'),
'w' => array('ŵ', 'ω', 'ώ'),
'x' => array('χ'),
'y' => array('ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ',
'ϋ', 'ύ', 'ΰ', 'ي'),
'z' => array('ź', 'ž', 'ż', 'з', 'ζ', 'ز'),
'aa' => array('ع'),
'ae' => array('æ'),
'ch' => array('ч'),
'dj' => array('ђ', 'đ'),
'dz' => array('џ'),
'gh' => array('غ'),
'kh' => array('х', 'خ'),
'lj' => array('љ'),
'nj' => array('њ'),
'oe' => array('œ'),
'ps' => array('ψ'),
'sh' => array('ш'),
'shch' => array('щ'),
'ss' => array('ß'),
'th' => array('þ', 'ث', 'ذ', 'ظ'),
'ts' => array('ц'),
'ya' => array('я'),
'yu' => array('ю'),
'zh' => array('ж'),
'(c)' => array('©'),
'A' => array('Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ',
'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Ä', 'Å', 'Ā',
'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ',
'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ',
'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А'),
'B' => array('Б', 'Β'),
'C' => array('Ç', 'Ć', 'Č', 'Ĉ', 'Ċ'),
'D' => array('Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ'),
'E' => array('É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ',
'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ',
'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э',
'Є', 'Ə'),
'F' => array('Ф', 'Φ'),
'G' => array('Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ'),
'H' => array('Η', 'Ή'),
'I' => array('Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į',
'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ',
'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї'),
'K' => array('К', 'Κ'),
'L' => array('Ĺ', 'Ł', 'Л', 'Λ', 'Ļ'),
'M' => array('М', 'Μ'),
'N' => array('Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν'),
'O' => array('Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ',
'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ö', 'Ø', 'Ō',
'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ',
'Ὸ', 'Ό', 'О', 'Θ', 'Ө'),
'P' => array('П', 'Π'),
'R' => array('Ř', 'Ŕ', 'Р', 'Ρ'),
'S' => array('Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ'),
'T' => array('Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ'),
'U' => array('Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ',
'Ự', 'Û', 'Ü', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У'),
'V' => array('В'),
'W' => array('Ω', 'Ώ'),
'X' => array('Χ'),
'Y' => array('Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ',
'Ы', 'Й', 'Υ', 'Ϋ'),
'Z' => array('Ź', 'Ž', 'Ż', 'З', 'Ζ'),
'AE' => array('Æ'),
'CH' => array('Ч'),
'DJ' => array('Ђ'),
'DZ' => array('Џ'),
'KH' => array('Х'),
'LJ' => array('Љ'),
'NJ' => array('Њ'),
'PS' => array('Ψ'),
'SH' => array('Ш'),
'SHCH' => array('Щ'),
'SS' => array('ẞ'),
'TH' => array('Þ'),
'TS' => array('Ц'),
'YA' => array('Я'),
'YU' => array('Ю'),
'ZH' => array('Ж'),
' ' => array("\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81",
"\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84",
"\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87",
"\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A",
"\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80"),
);
}
}

5
libs/Exception.php Normal file
View File

@ -0,0 +1,5 @@
<?php namespace Todaymade\Daux;
class Exception extends \Exception
{
}

View File

@ -0,0 +1,71 @@
<?php namespace Todaymade\Daux\Format\Base;
use Todaymade\Daux\Config;
use Todaymade\Daux\ContentTypes\ContentType;
use Todaymade\Daux\Tree\Content;
abstract class ContentPage extends SimplePage
{
/**
* @var Content
*/
protected $file;
/**
* @var Config
*/
protected $params;
/**
* @var ContentType
*/
protected $contentType;
public function __construct($title, $content)
{
$this->initializePage($title, $content);
}
public function setFile(Content $file)
{
$this->file = $file;
}
public function getFile()
{
return $this->file;
}
public function setParams(Config $params)
{
$this->params = $params;
}
/**
* @param ContentType $contentType
*/
public function setContentType($contentType)
{
$this->contentType = $contentType;
}
protected function convertPage($content)
{
return $this->contentType->convert($content, $this->getFile());
}
protected function generatePage()
{
return $this->convertPage($this->content);
}
public static function fromFile(Content $file, $params, ContentType $contentType)
{
$page = new static($file->getTitle(), $file->getContent());
$page->setFile($file);
$page->setParams($params);
$page->setContentType($contentType);
return $page;
}
}

View File

@ -0,0 +1,82 @@
<?php
/**
* Created by IntelliJ IDEA.
* User: onigoetz
* Date: 06/11/15
* Time: 20:27
*/
namespace Todaymade\Daux\Format\Base;
use DOMDocument;
use Todaymade\Daux\DauxHelper;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Root;
class EmbedImages
{
protected $tree;
public function __construct(Root $tree)
{
$this->tree = $tree;
}
public function embed($page, Content $file, $callback)
{
return preg_replace_callback(
"/<img\\s+[^>]*src=['\"]([^\"]*)['\"][^>]*>/",
function ($matches) use ($file, $callback) {
if ($result = $this->findImage($matches[1], $matches[0], $file, $callback)) {
return $result;
}
return $matches[0];
},
$page
);
}
private function getAttributes($tag)
{
$dom = new DOMDocument();
$dom->loadHTML($tag);
$img = $dom->getElementsByTagName('img')->item(0);
$attributes = ['align', 'class', 'title', 'style', 'alt', 'height', 'width'];
$used = [];
foreach ($attributes as $attr) {
if ($img->attributes->getNamedItem($attr)) {
$used[$attr] = $img->attributes->getNamedItem($attr)->value;
}
}
return $used;
}
private function findImage($src, $tag, Content $file, $callback)
{
//for protocol relative or http requests : keep the original one
if (substr($src, 0, strlen("http")) === "http" || substr($src, 0, strlen("//")) === "//") {
return $src;
}
//Get the path to the file, relative to the root of the documentation
$url = DauxHelper::getCleanPath(dirname($file->getUrl()) . '/' . $src);
//Get any file corresponding to the right one
$file = DauxHelper::getFile($this->tree, $url);
if ($file === false) {
return false;
}
$result = $callback($src, $this->getAttributes($tag), $file);
return $result ?: $src;
}
}

View File

@ -0,0 +1,26 @@
<?php namespace Todaymade\Daux\Format\Base;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Daux;
interface Generator
{
/**
* @param Daux $daux
*/
public function __construct(Daux $daux);
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param integer $width
* @return mixed
*/
public function generateAll(InputInterface $input, OutputInterface $output, $width);
/**
* @return array
*/
public function getContentTypes();
}

View File

@ -0,0 +1,14 @@
<?php namespace Todaymade\Daux\Format\Base;
use Todaymade\Daux\Config;
use Todaymade\Daux\Tree\Entry;
interface LiveGenerator extends Generator
{
/**
* @param Entry $node
* @param Config $params
* @return \Todaymade\Daux\Format\Base\Page
*/
public function generateOne(Entry $node, Config $params);
}

View File

@ -0,0 +1,6 @@
<?php namespace Todaymade\Daux\Format\Base;
interface Page
{
public function getContent();
}

View File

@ -0,0 +1,23 @@
<?php namespace Todaymade\Daux\Format\Base;
use Todaymade\Daux\Exception;
abstract class RawPage implements Page
{
protected $file;
public function __construct($filename)
{
$this->file = $filename;
}
public function getFile()
{
return $this->file;
}
public function getContent()
{
throw new Exception("you should not use this method to show a raw content");
}
}

View File

@ -0,0 +1,33 @@
<?php namespace Todaymade\Daux\Format\Base;
abstract class SimplePage implements Page
{
protected $title;
protected $content;
protected $generated = null;
public function __construct($title, $content)
{
$this->initializePage($title, $content);
}
public function getContent()
{
if (is_null($this->generated)) {
$this->generated = $this->generatePage();
}
return $this->generated;
}
protected function initializePage($title, $content)
{
$this->title = $title;
$this->content = $content;
}
protected function generatePage()
{
return $this->content;
}
}

View File

@ -0,0 +1,224 @@
<?php namespace Todaymade\Daux\Format\Confluence;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ParseException;
class Api
{
protected $base_url;
protected $user;
protected $pass;
protected $space;
public function __construct($base_url, $user, $pass)
{
$this->base_url = $base_url;
$this->user = $user;
$this->pass = $pass;
}
public function setSpace($space_id)
{
$this->space = $space_id;
}
protected function getClient()
{
$options = [
'base_url' => $this->base_url . 'rest/api/',
'defaults' => [
'auth' => [$this->user, $this->pass]
]
];
return new Client($options);
}
/**
* The standard error message from guzzle is quite poor in informations,
* this will give little bit more sense to it and return it
*
* @param BadResponseException $e
* @return BadResponseException
*/
protected function handleError(BadResponseException $e)
{
$request = $e->getRequest();
$response = $e->getResponse();
$level = floor($response->getStatusCode() / 100);
if ($level == '4') {
$label = 'Client error response';
} elseif ($level == '5') {
$label = 'Server error response';
} else {
$label = 'Unsuccessful response';
}
$message = $label .
' [url] ' . $request->getUrl() .
' [status code] ' . $response->getStatusCode() .
' [message] ';
try {
$message .= $response->json()['message'];
} catch (ParseException $e) {
$message .= (string) $response->getBody();
}
return new BadResponseException($message, $request, $response, $e->getPrevious());
}
/**
* Get a list of pages
*
* @param integer $rootPage
* @param bool $recursive
* @return array
*/
public function getList($rootPage, $recursive = false)
{
$increment = 15;
// We set a limit of 15 as it appears that
// Confluence fails silently when retrieving
// more than 20 entries with "body.storage"
$base_url = $url = "content/$rootPage/child/page?expand=version,body.storage&limit=$increment";
$start = 0;
$pages = [];
do {
try {
$hierarchy = $this->getClient()->get($url)->json();
} catch (BadResponseException $e) {
throw $this->handleError($e);
}
foreach ($hierarchy['results'] as $result) {
$pages[$result['title']] = [
"id" => $result['id'],
"title" => $result['title'],
"version" => $result['version']['number'],
"content" => $result['body']['storage']['value'],
];
if ($recursive) {
$pages[$result['title']]['children'] = $this->getList($result['id'], true);
}
}
// We don't use _links->next as after ~30 elements
// it doesn't show any new elements. This seems
// to be a bug in Confluence
$start += $increment;
$url = "$base_url&start=$start";
} while (!empty($hierarchy['results']));
return $pages;
}
/**
* @param integer $parent_id
* @param string $title
* @param string $content
* @return integer
*/
public function createPage($parent_id, $title, $content)
{
$body = [
'type' => 'page',
'space' => ['key' => $this->space],
'ancestors' => [['type' => 'page', 'id' => $parent_id]],
'title' => $title,
'body' => ['storage' => ['value' => $content, 'representation' => 'storage']]
];
try {
$response = $this->getClient()->post('content', ['json' => $body])->json();
} catch (BadResponseException $e) {
throw $this->handleError($e);
}
return $response['id'];
}
/**
* @param integer $parent_id
* @param integer $page_id
* @param integer $newVersion
* @param string $title
* @param string $content
*/
public function updatePage($parent_id, $page_id, $newVersion, $title, $content)
{
$body = [
'type' => 'page',
'space' => ['key' => $this->space],
'ancestors' => [['type' => 'page', 'id' => $parent_id]],
'version' => ['number' => $newVersion, "minorEdit" => true],
'title' => $title,
'body' => ['storage' => ['value' => $content, 'representation' => 'storage']]
];
try {
$this->getClient()->put("content/$page_id", ['json' => $body])->json();
} catch (BadResponseException $e) {
throw $this->handleError($e);
}
}
/**
* Delete a page
*
* @param integer $page_id
* @return mixed
*/
public function deletePage($page_id)
{
try {
return $this->getClient()->delete('content/' . $page_id)->json();
} catch (BadResponseException $e) {
throw $this->handleError($e);
}
}
/**
* @param integer $id
* @param array $attachment
*/
public function uploadAttachment($id, $attachment)
{
// Check if an attachment with
// this name is uploaded
try {
$result = $this->getClient()->get("content/$id/child/attachment?filename=$attachment[filename]")->json();
} catch (BadResponseException $e) {
throw $this->handleError($e);
}
$url = "content/$id/child/attachment";
// If the attachment is already uploaded,
// the update URL is different
if (count($result['results'])) {
$url .= "/{$result['results'][0]['id']}/data";
}
try {
$this->getClient()->post(
$url,
[
'body' => ['file' => fopen($attachment['file']->getPath(), 'r')],
'headers' => ['X-Atlassian-Token' => 'nocheck'],
]
);
} catch (BadResponseException $e) {
throw $this->handleError($e);
}
}
}

View File

@ -0,0 +1,45 @@
<?php namespace Todaymade\Daux\Format\Confluence;
use Todaymade\Daux\Format\Base\EmbedImages;
use Todaymade\Daux\Tree\Raw;
class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
{
public $attachments = [];
protected function generatePage()
{
$content = parent::generatePage();
//Embed images
// We do it after generation so we can catch the images that were in html already
$content = (new EmbedImages($this->params['tree']))
->embed(
$content,
$this->file,
function ($src, array $attributes, Raw $file) {
$filename = basename($file->getPath());
//Add the attachment for later upload
$this->attachments[] = ['filename' => $filename, 'file' => $file];
return $this->createImageTag($filename, $attributes);
}
);
return $content;
}
private function createImageTag($filename, $attributes)
{
$img = "<ac:image";
foreach ($attributes as $name => $value) {
$img .= ' ac:' . $name . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
}
$img .= "><ri:attachment ri:filename=\"$filename\" /></ac:image>";
return $img;
}
}

View File

@ -0,0 +1,22 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use League\CommonMark\Environment;
class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter
{
protected function getLinkRenderer(Environment $environment)
{
return new LinkRenderer($environment->getConfig('daux'));
}
protected function extendEnvironment(Environment $environment)
{
parent::extendEnvironment($environment);
//Add code renderer
$environment->addBlockRenderer('FencedCode', new FencedCodeRenderer());
$environment->addBlockRenderer('IndentedCode', new IndentedCodeRenderer());
$environment->addInlineRenderer('Image', new ImageRenderer());
}
}

View File

@ -0,0 +1,12 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use Todaymade\Daux\Config;
class ContentType extends \Todaymade\Daux\ContentTypes\Markdown\ContentType
{
public function __construct(Config $config)
{
$this->config = $config;
$this->converter = new CommonMarkConverter(['daux' => $config]);
}
}

View File

@ -0,0 +1,85 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use League\CommonMark\Block\Element\AbstractBlock;
use League\CommonMark\Block\Element\FencedCode;
use League\CommonMark\Block\Renderer\BlockRendererInterface;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement;
class FencedCodeRenderer implements BlockRendererInterface
{
protected $supported_languages = [
'actionscript3',
'bash',
'csharp',
'coldfusion',
'cpp',
'css',
'delphi',
'diff',
'erlang',
'groovy',
'html/xml',
'java',
'javafx',
'javascript',
'none',
'perl',
'php',
'powershell',
'python',
'ruby',
'scala',
'sql',
'vb'
];
protected $known_conversions = ['html' => 'html/xml', 'xml' => 'html/xml', 'js' => 'javascript'];
/**
* @param AbstractBlock $block
* @param HtmlRendererInterface $htmlRenderer
* @param bool $inTightList
*
* @return HtmlElement|string
*/
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
{
if (!($block instanceof FencedCode)) {
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
}
$content = [];
if ($language = $this->getLanguage($block->getInfoWords(), $htmlRenderer)) {
$content[] = new HtmlElement('ac:parameter', ['ac:name' => 'language'], $language);
}
$content[] = new HtmlElement('ac:plain-text-body', [], '<![CDATA[' . $block->getStringContent() . ']]>');
return new HtmlElement(
'ac:structured-macro',
['ac:name' => 'code'],
$content
);
}
public function getLanguage($infoWords, ElementRendererInterface $htmlRenderer)
{
if (count($infoWords) === 0 || strlen($infoWords[0]) === 0) {
return false;
}
$language = $htmlRenderer->escape($infoWords[0], true);
if (array_key_exists($language, $this->known_conversions)) {
$language = $this->known_conversions[$language];
}
if (in_array($language, $this->supported_languages)) {
return $language;
}
return false;
}
}

View File

@ -0,0 +1,29 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement;
use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Image;
class ImageRenderer extends \League\CommonMark\Inline\Renderer\ImageRenderer
{
/**
* @param Image $inline
* @param ElementRendererInterface $htmlRenderer
*
* @return HtmlElement
*/
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
{
// External Images need special handling
if (strpos($inline->getUrl(), 'http') === 0) {
return new HtmlElement(
'ac:image',
[],
new HtmlElement('ri:url', ['ri:value' => $inline->getUrl()])
);
}
return parent::render($inline, $htmlRenderer);
}
}

View File

@ -0,0 +1,30 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use League\CommonMark\Block\Element\AbstractBlock;
use League\CommonMark\Block\Element\IndentedCode;
use League\CommonMark\Block\Renderer\BlockRendererInterface;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement;
class IndentedCodeRenderer implements BlockRendererInterface
{
/**
* @param AbstractBlock $block
* @param HtmlRendererInterface $htmlRenderer
* @param bool $inTightList
*
* @return HtmlElement
*/
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
{
if (!($block instanceof IndentedCode)) {
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
}
return new HtmlElement(
'ac:structured-macro',
['ac:name' => 'code'],
new HtmlElement('ac:plain-text-body', [], '<![CDATA[' . $block->getStringContent() . ']]>')
);
}
}

View File

@ -0,0 +1,54 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use League\CommonMark\HtmlElement;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Link;
class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
{
/**
* @param Link $inline
* @param ElementRendererInterface $htmlRenderer
*
* @return HtmlElement
*/
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
{
// This can't be in the method type as
// the method is an abstract and should
// have the same interface
if (!$inline instanceof Link) {
throw new \RuntimeException(
"Wrong type passed to " . __CLASS__ . "::" . __METHOD__ .
" the expected type was 'League\\CommonMark\\Inline\\Element\\Link' but '" .
get_class($inline) . "' was provided"
);
}
// Default handling
$element = parent::render($inline, $htmlRenderer);
$url = $inline->getUrl();
if (empty($url) || $url[0] != '!') {
return $element;
}
//Internal links
$file = $this->resolveInternalFile(ltrim($url, "!"));
$link_props = [
'ri:content-title' => trim($this->daux['confluence']['prefix']) . " " . $file->getTitle(),
'ri:space-key' => $this->daux['confluence']['space_id']
];
$page = strval(new HtmlElement('ri:page', $link_props, '', true));
$children = $htmlRenderer->renderInlines($inline->children());
if (strpos($children, "<") !== false) {
$children = '<ac:link-body>' . $children . '</ac:link-body>';
} else {
$children = '<ac:plain-text-link-body><![CDATA[' . $children . ']]></ac:plain-text-link-body>';
}
return new HtmlElement('ac:link', [], $page . $children);
}
}

View File

@ -0,0 +1,112 @@
<?php namespace Todaymade\Daux\Format\Confluence;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Config;
use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\Daux;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
class Generator implements \Todaymade\Daux\Format\Base\Generator
{
use RunAction;
/** @var string */
protected $prefix;
/** @var Daux */
protected $daux;
/**
* @param Daux $daux
*/
public function __construct(Daux $daux)
{
$this->daux = $daux;
}
/**
* @return array
*/
public function getContentTypes()
{
return [
new ContentTypes\Markdown\ContentType($this->daux->getParams())
];
}
/**
* {@inheritdoc}
*/
public function generateAll(InputInterface $input, OutputInterface $output, $width)
{
$params = $this->daux->getParams();
$confluence = $params['confluence'];
$this->prefix = trim($confluence['prefix']) . " ";
$tree = $this->runAction(
"Generating Tree ...",
$output,
$width,
function() use ($params) {
$tree = $this->generateRecursive($this->daux->tree, $params);
$tree['title'] = $this->prefix . $params['title'];
return $tree;
}
);
$output->writeln("Start Publishing...");
$publisher = new Publisher($confluence);
$publisher->output = $output;
$publisher->width = $width;
$publisher->publish($tree);
}
private function generateRecursive(Directory $tree, Config $params, $base_url = '')
{
$final = ['title' => $this->prefix . $tree->getTitle()];
$params['base_url'] = $params['base_page'] = $base_url;
$params['image'] = str_replace('<base_url>', $base_url, $params['image']);
if ($base_url !== '') {
$params['entry_page'] = $tree->getFirstPage();
}
foreach ($tree->getEntries() as $key => $node) {
if ($node instanceof Directory) {
$final['children'][$this->prefix . $node->getTitle()] = $this->generateRecursive(
$node,
$params,
'../' . $base_url
);
} elseif ($node instanceof Content) {
$params['request'] = $node->getUrl();
$contentType = $this->daux->getContentTypeHandler()->getType($node);
$data = [
'title' => $this->prefix . $node->getTitle(),
'file' => $node,
'page' => ContentPage::fromFile($node, $params, $contentType),
];
// As the page is lazily generated
// We do it now to fail fast in case of problem
$data['page']->getContent();
if ($key == 'index.html') {
$final['title'] = $this->prefix . $tree->getTitle();
$final['file'] = $node;
$final['page'] = $data['page'];
} else {
$final['children'][$data['title']] = $data;
}
}
}
return $final;
}
}

View File

@ -0,0 +1,243 @@
<?php namespace Todaymade\Daux\Format\Confluence;
use GuzzleHttp\Exception\BadResponseException;
use Todaymade\Daux\Console\RunAction;
class Publisher
{
use RunAction;
/**
* @var Api
*/
protected $client;
/**
* @var array
*/
protected $confluence;
/**
* @var string
*/
protected $previous_title;
/**
* @var integer terminal width
*/
public $width;
/**
* @var
*/
public $output;
/**
* @param $confluence
*/
public function __construct($confluence)
{
$this->confluence = $confluence;
$this->client = new Api($confluence['base_url'], $confluence['user'], $confluence['pass']);
$this->client->setSpace($confluence['space_id']);
}
public function run($title, $closure)
{
try {
return $this->runAction($title, $this->output, $this->width, $closure);
} catch (BadResponseException $e) {
$this->output->writeLn(" <error>" . $e->getMessage() . "</error>");
}
}
public function publish(array $tree)
{
echo "Finding Root Page...\n";
$pages = $this->client->getList($this->confluence['ancestor_id']);
$published = null;
foreach ($pages as $page) {
if ($page['title'] == $tree['title']) {
$published = $page;
break;
}
}
$this->run(
"Getting already published pages...",
function() use (&$published) {
if ($published != null) {
$published['children'] = $this->client->getList($published['id'], true);
}
}
);
$published = $this->run(
"Create placeholder pages...",
function() use ($tree, $published) {
return $this->createRecursive($this->confluence['ancestor_id'], $tree, $published);
}
);
$this->output->writeLn("Publishing updates...");
$this->updateRecursive($this->confluence['ancestor_id'], $tree, $published);
}
protected function niceTitle($title)
{
if ($title == "index.html") {
return "Homepage";
}
return rtrim(strtr($title, ['index.html' => '', '.html' => '']), "/");
}
protected function createPage($parent_id, $entry, $published)
{
echo "- " . $this->niceTitle($entry['file']->getUrl()) . "\n";
$published['version'] = 1;
$published['id'] = $this->client->createPage($parent_id, $entry['title'], "The content will come very soon !");
return $published;
}
protected function createPlaceholderPage($parent_id, $entry, $published)
{
echo "- " . $entry['title'] . "\n";
$published['version'] = 1;
$published['id'] = $this->client->createPage($parent_id, $entry['title'], "");
return $published;
}
protected function recursiveWithCallback($parent_id, $entry, $published, $callback)
{
$published = $callback($parent_id, $entry, $published);
if (array_key_exists('children', $entry)) {
foreach ($entry['children'] as $child) {
$pub = [];
if (isset($published['children']) && array_key_exists($child['title'], $published['children'])) {
$pub = $published['children'][$child['title']];
}
$published['children'][$child['title']] = $this->recursiveWithCallback(
$published['id'],
$child,
$pub,
$callback
);
}
}
return $published;
}
protected function createRecursive($parent_id, $entry, $published)
{
$callback = function($parent_id, $entry, $published) {
//TODO :: remove deleted pages
// nothing to do if the ID already exists
if (array_key_exists('id', $published)) {
return $published;
}
if (array_key_exists('page', $entry)) {
return $this->createPage($parent_id, $entry, $published);
}
// If we have no $entry['page'] it means the page
// doesn't exist, but to respect the hierarchy,
// we need a blank page there
return $this->createPlaceholderPage($parent_id, $entry, $published);
};
return $this->recursiveWithCallback($parent_id, $entry, $published, $callback);
}
protected function updateRecursive($parent_id, $entry, $published)
{
$callback = function($parent_id, $entry, $published) {
if (array_key_exists('id', $published) && array_key_exists('page', $entry)) {
$this->updatePage($parent_id, $entry, $published);
}
return $published;
};
return $this->recursiveWithCallback($parent_id, $entry, $published, $callback);
}
protected function shouldUpdate($local, $published)
{
if (!array_key_exists('content', $published)) {
return true;
}
$trimmed_local = trim($local->getContent());
$trimmed_distant = trim($published['content']);
if ($trimmed_local == $trimmed_distant) {
return false;
}
similar_text($trimmed_local, $trimmed_distant, $percent);
// I consider that if the files are 98% identical you
// don't need to update. This will work for false positives.
// But sadly will miss if it's just a typo update
if ($percent >= 98) {
return false;
}
//DEBUG
if (getenv("DEBUG") && strtolower(getenv("DEBUG")) != "false") {
$prefix = 'static/export/';
if (!is_dir($prefix)) {
mkdir($prefix, 0777, true);
}
$url = $local->getFile()->getUrl();
file_put_contents($prefix . strtr($url, ['/' => '_', '.html' => '_local.html']), $trimmed_local);
file_put_contents($prefix . strtr($url, ['/' => '_', '.html' => '_distant.html']), $trimmed_distant);
}
return true;
}
protected function updatePage($parent_id, $entry, $published)
{
if ($this->previous_title != "Updating") {
$this->previous_title = "Updating";
echo "Updating Pages...\n";
}
$this->run(
"- " . $this->niceTitle($entry['file']->getUrl()),
function() use ($entry, $published, $parent_id) {
if ($this->shouldUpdate($entry['page'], $published)) {
$this->client->updatePage(
$parent_id,
$published['id'],
$published['version'] + 1,
$entry['title'],
$entry['page']->getContent()
);
}
}
);
if (count($entry['page']->attachments)) {
foreach ($entry['page']->attachments as $attachment) {
$this->run(
" With attachment: $attachment[filename]",
function() use ($published, $attachment) {
$this->client->uploadAttachment($published['id'], $attachment);
}
);
}
}
}
}

View File

@ -0,0 +1,92 @@
<?php namespace Todaymade\Daux\Format\HTML;
use Todaymade\Daux\Tree\Root;
class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
{
private $language;
private $homepage;
private function initialize()
{
$this->homepage = false;
if ($this->file->getParent()->getIndexPage() == $this->file) {
if ($this->params['multilanguage']) {
if ($this->file->getParent()->getParent() instanceof Root) {
$this->homepage = true;
}
} elseif ($this->file->getParent() instanceof Root) {
$this->homepage = true;
}
}
$this->language = '';
if ($this->params['multilanguage'] && count($this->file->getParents())) {
$language_dir = $this->file->getParents()[0];
$this->language = $language_dir->getName();
}
}
/**
* @param \Todaymade\Daux\Tree\Directory[] $parents
* @param bool $multilanguage
* @return array
*/
private function getBreadcrumbTrail($parents, $multilanguage)
{
if ($multilanguage && !empty($parents)) {
$parents = array_splice($parents, 1);
}
$breadcrumb_trail = array();
if (!empty($parents)) {
foreach ($parents as $node) {
$page = $node->getIndexPage() ?: $node->getFirstPage();
$breadcrumb_trail[$node->getTitle()] = $page? $page->getUrl() : '';
}
}
return $breadcrumb_trail;
}
protected function generatePage()
{
$this->initialize();
$params = $this->params;
$entry_page = [];
if ($this->homepage) {
if ($params['multilanguage']) {
foreach ($params['languages'] as $key => $name) {
$entry_page[$name] = $params['base_page'] . $params['entry_page'][$key]->getUrl();
}
} else {
$entry_page['View Documentation'] = $params['base_page'] . $params['entry_page']->getUrl();
}
}
$page = [
'entry_page' => $entry_page,
'homepage' => $this->homepage,
'title' => $this->file->getTitle(),
'filename' => $this->file->getName(),
'language' => $this->language,
'path' => $this->file->getPath(),
'modified_time' => filemtime($this->file->getPath()),
'markdown' => $this->content,
'request' => $params['request'],
'content' => $this->convertPage($this->content),
'breadcrumbs' => $params['html']['breadcrumbs'],
'prev' => $this->file->getPrevious(),
'next' => $this->file->getNext(),
];
if ($page['breadcrumbs']) {
$page['breadcrumb_trail'] = $this->getBreadcrumbTrail($this->file->getParents(), $params['multilanguage']);
$page['breadcrumb_separator'] = $params['html']['breadcrumb_separator'];
}
$context = ['page' => $page, 'params' => $params];
$template = new Template($params['templates'], $params['theme']['templates']);
return $template->render($this->homepage ? 'theme::home' : 'theme::content', $context);
}
}

View File

@ -0,0 +1,124 @@
<?php namespace Todaymade\Daux\Format\HTML;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Config;
use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\ContentTypes\Markdown\ContentType;
use Todaymade\Daux\Daux;
use Todaymade\Daux\DauxHelper;
use Todaymade\Daux\Format\Base\LiveGenerator;
use Todaymade\Daux\GeneratorHelper;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
use Todaymade\Daux\Tree\Entry;
use Todaymade\Daux\Tree\Raw;
class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
{
use RunAction;
/** @var Daux */
protected $daux;
/**
* @param Daux $daux
*/
public function __construct(Daux $daux)
{
$this->daux = $daux;
}
/**
* @return array
*/
public function getContentTypes()
{
return [
'markdown' => new ContentType($this->daux->getParams())
];
}
public function generateAll(InputInterface $input, OutputInterface $output, $width)
{
$destination = $input->getOption('destination');
$params = $this->daux->getParams();
if (is_null($destination)) {
$destination = $this->daux->local_base . DIRECTORY_SEPARATOR . 'static';
}
$this->runAction(
"Copying Static assets ...",
$output,
$width,
function() use ($destination) {
GeneratorHelper::copyAssets($destination, $this->daux->local_base);
}
);
$output->writeLn("Generating ...");
$this->generateRecursive($this->daux->tree, $destination, $params, $output, $width);
}
/**
* Recursively generate the documentation
*
* @param Directory $tree
* @param string $output_dir
* @param \Todaymade\Daux\Config $params
* @param OutputInterface $output
* @param integer $width
* @param string $base_url
* @throws \Exception
*/
private function generateRecursive(Directory $tree, $output_dir, $params, $output, $width, $base_url = '')
{
DauxHelper::rebaseConfiguration($params, $base_url);
if ($base_url !== '' && empty($params['entry_page'])) {
$params['entry_page'] = $tree->getFirstPage();
}
foreach ($tree->getEntries() as $key => $node) {
if ($node instanceof Directory) {
$new_output_dir = $output_dir . DIRECTORY_SEPARATOR . $key;
mkdir($new_output_dir);
$this->generateRecursive($node, $new_output_dir, $params, $output, $width, '../' . $base_url);
// Rebase configuration again as $params is a shared object
DauxHelper::rebaseConfiguration($params, $base_url);
} else {
$this->runAction(
"- " . $node->getUrl(),
$output,
$width,
function() use ($node, $output_dir, $key, $params) {
if (!$node instanceof Content) {
copy($node->getPath(), $output_dir . DIRECTORY_SEPARATOR . $key);
return;
}
$generated = $this->generateOne($node, $params);
file_put_contents($output_dir . DIRECTORY_SEPARATOR . $key, $generated->getContent());
}
);
}
}
}
/**
* @param Entry $node
* @param Config $params
* @return \Todaymade\Daux\Format\Base\Page
*/
public function generateOne(Entry $node, Config $params)
{
if ($node instanceof Raw) {
return new RawPage($node->getPath());
}
$params['request'] = $node->getUrl();
return ContentPage::fromFile($node, $params, $this->daux->getContentTypeHandler()->getType($node));
}
}

View File

@ -0,0 +1,6 @@
<?php namespace Todaymade\Daux\Format\HTML;
class RawPage extends \Todaymade\Daux\Format\Base\RawPage
{
}

View File

@ -0,0 +1,5 @@
<?php namespace Todaymade\Daux\Format\HTML;
class SimplePage extends \Todaymade\Daux\Format\Base\SimplePage
{
}

View File

@ -0,0 +1,153 @@
<?php namespace Todaymade\Daux\Format\HTML;
use League\Plates\Engine;
use Todaymade\Daux\Daux;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
class Template
{
protected $engine;
/**
* @param string $base
* @param string $theme
*/
public function __construct($base, $theme)
{
// Create new Plates instance
$this->engine = new Engine($base);
if (!is_dir($theme)) {
$theme = $base;
}
$this->engine->addFolder('theme', $theme, true);
$this->registerFunctions();
}
/**
* @param string $name
* @param array $data
* @return string
*/
public function render($name, array $data = array())
{
$this->engine->addData([
'base_url' => $data['params']['base_url'],
'base_page' => $data['params']['base_page'],
'page' => $data['page'],
'params' => $data['params'],
'tree' => $data['params']['tree'],
]);
return $this->engine->render($name, $data);
}
protected function registerFunctions()
{
$this->engine->registerFunction('get_navigation', function($tree, $path, $current_url, $base_page, $mode) {
$nav = $this->buildNavigation($tree, $path, $current_url, $base_page, $mode);
return $this->renderNavigation($nav);
});
$this->engine->registerFunction('get_breadcrumb_title', function($page, $base_page) {
$title = '';
$breadcrumb_trail = $page['breadcrumb_trail'];
$separator = $this->getSeparator($page['breadcrumb_separator']);
foreach ($breadcrumb_trail as $key => $value) {
$title .= '<a href="' . $base_page . $value . '">' . $key . '</a>' . $separator;
}
if ($page['filename'] === 'index' || $page['filename'] === '_index') {
if ($page['title'] != '') {
$title = substr($title, 0, -1 * strlen($separator));
}
} else {
$title .= '<a href="' . $base_page . $page['request'] . '">' . $page['title'] . '</a>';
}
return $title;
});
}
private function renderNavigation($entries)
{
$nav = "";
foreach ($entries as $entry) {
if (array_key_exists('children', $entry)) {
if (array_key_exists('href', $entry)) {
$link = '<a href="' . $entry['href'] . '" class="folder">' . $entry['title'] . '</a>';
} else {
$link = '<a href="#" class="aj-nav folder">' . $entry['title'] . '</a>';
}
$link .= $this->renderNavigation($entry['children']);
} else {
$link = '<a href="' . $entry['href'] . '">' . $entry['title'] . '</a>';
}
$nav .= "<li class='$entry[class]'>$link</li>";
}
return "<ul class='nav nav-list'>$nav</ul>";
}
private function buildNavigation(Directory $tree, $path, $current_url, $base_page, $mode)
{
$nav = [];
foreach ($tree->getEntries() as $node) {
$url = $node->getUri();
if ($node instanceof Content) {
if ($node->isIndex()) {
continue;
}
$link = ($path === '') ? $url : $path . '/' . $url;
$nav[] = [
'title' => $node->getTitle(),
'href' => $base_page . $link,
'class' => ($current_url === $link) ? 'active' : ''
];
} elseif ($node instanceof Directory) {
if (!$node->hasContent()) {
continue;
}
$link = ($path === '') ? $url : $path . '/' . $url;
$folder = [
'title' => $node->getTitle(),
'class' => (strpos($current_url, $link) === 0) ? 'open' : '',
];
if ($mode === Daux::STATIC_MODE) {
$link .= "/index.html";
}
if ($node->getIndexPage()) {
$folder['href'] = $base_page . $link;
}
//Child pages
$new_path = ($path === '') ? $url : $path . '/' . $url;
$folder['children'] = $this->buildNavigation($node, $new_path, $current_url, $base_page, $mode);
$nav[] = $folder;
}
}
return $nav;
}
/**
* @param string $separator
* @return string
*/
private function getSeparator($separator)
{
switch ($separator) {
case 'Chevrons':
return ' <i class="glyphicon glyphicon-chevron-right"></i> ';
default:
return $separator;
}
}
}

View File

@ -0,0 +1,142 @@
<?php namespace Todaymade\Daux\Format\HTMLFile;
use RuntimeException;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
class Book
{
protected $cover;
protected $tree;
protected $pages = [];
public function __construct(Directory $tree, $cover)
{
$this->tree = $tree;
$this->cover = $cover;
}
protected function getStyles()
{
// TODO :: un-hardcode that
return '<style>' . file_get_contents('themes/daux_singlepage/css/main.min.css') . '</style>';
}
protected function getSectionId(Content $node)
{
foreach ($this->pages as $id => $page) {
if ($page['page'] == $node) {
return $id;
}
}
throw new RuntimeException("Could not find the content page");
}
protected function buildNavigation(Directory $tree)
{
$nav = [];
foreach ($tree->getEntries() as $node) {
if ($node instanceof Content) {
if ($node->isIndex()) {
continue;
}
$nav[] = [
'title' => $node->getTitle(),
'href' => '#section_' . $this->getSectionId($node),
];
} elseif ($node instanceof Directory) {
if (!$node->hasContent()) {
continue;
}
$page_index = ($index = $node->getIndexPage())? $index : $node->getFirstPage();
$nav[] = [
'title' => $node->getTitle(),
'href' => "#section_" . $this->getSectionId($page_index),
'children' => $this->buildNavigation($node)
];
}
}
return $nav;
}
private function renderNavigation($entries)
{
$nav = "";
foreach ($entries as $entry) {
if (array_key_exists('children', $entry)) {
if (array_key_exists('href', $entry)) {
$link = '<a href="' . $entry['href'] . '" class="folder">' . $entry['title'] . '</a>';
} else {
$link = '<a href="#" class="aj-nav folder">' . $entry['title'] . '</a>';
}
$link .= $this->renderNavigation($entry['children']);
} else {
$link = '<a href="' . $entry['href'] . '">' . $entry['title'] . '</a>';
}
$nav .= "<li>$link</li>";
}
return "<ul>$nav</ul>";
}
protected function generateTOC()
{
return "<h1>Table of Contents</h1>" .
$this->renderNavigation($this->buildNavigation($this->tree)) .
"</div><div class=\"page-break\">&nbsp;</div>";
}
protected function generateCover()
{
return "<div style='margin:4em 30% 4em 0;'>" .
"<h1 style='font-size:40pt; margin-bottom:0;'>{$this->cover['title']}</h1>" .
"<p><strong>{$this->cover['subject']}</strong> by {$this->cover['author']}</p>" .
"</div><div class=\"page-break\">&nbsp;</div>";
}
protected function generatePages()
{
$content = '';
foreach ($this->pages as $section => $page) {
$content .= '<a id="section_' . $section . '"></a>';
$content .= '<h1>' . $page['page']->getTitle() . '</h1>';
$content .= '<section class="content">' . $page['content'] . '</section>';
$content .= '<div class="page-break">&nbsp;</div>';
}
return $content;
}
public function addPage($page, $content)
{
$this->pages[] = ['page' => $page, 'content' => $content];
}
public function generateHead()
{
$head = [
"<title>{$this->cover['title']}</title>",
"<meta name='description' content='{$this->cover['subject']}' />",
"<meta name='author' content='{$this->cover['author']}'>",
"<meta charset='UTF-8'>",
$this->getStyles(),
];
return '<head>' . implode('', $head) . '</head>';
}
public function generateBody()
{
return '<body>' . $this->generateCover() . $this->generateTOC() . $this->generatePages() . '</body>';
}
public function generate()
{
return "<!DOCTYPE html><html>" . $this->generateHead() . $this->generateBody() . "</html>";
}
}

View File

@ -0,0 +1,34 @@
<?php namespace Todaymade\Daux\Format\HTMLFile;
use Todaymade\Daux\Format\Base\EmbedImages;
use Todaymade\Daux\Tree\Raw;
class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
{
public $attachments = [];
protected function generatePage()
{
$content = parent::generatePage();
//Embed images
// We do it after generation so we can catch the images that were in html already
$content = (new EmbedImages($this->params['tree']))
->embed(
$content,
$this->file,
function ($src, array $attributes, Raw $file) {
$content = base64_encode(file_get_contents($file->getPath()));
$attr = '';
foreach ($attributes as $name => $value) {
$attr .= ' ' .$name . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
}
return "<img $attr src=\"data:image/png;base64,$content\"/>";
}
);
return $content;
}
}

View File

@ -0,0 +1,103 @@
<?php namespace Todaymade\Daux\Format\HTMLFile;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\ContentTypes\Markdown\ContentType;
use Todaymade\Daux\Daux;
class Generator implements \Todaymade\Daux\Format\Base\Generator
{
use RunAction;
/** @var Daux */
protected $daux;
/**
* @param Daux $daux
*/
public function __construct(Daux $daux)
{
$this->daux = $daux;
}
/**
* @return array
*/
public function getContentTypes()
{
return [
'markdown' => new ContentType($this->daux->getParams())
];
}
protected function initPDF()
{
// create new PDF document
$pdf = new Book(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$params = $this->daux->getParams();
// set document information
$pdf->SetCreator(PDF_CREATOR);
// set default header data
$pdf->SetHeaderData('', 0, $params['title'], $params['tagline']);
// set header and footer fonts
$pdf->setHeaderFont(array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// set auto page breaks
$pdf->SetAutoPageBreak(true, PDF_MARGIN_BOTTOM);
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// set font
$pdf->SetFont('helvetica', '', 10);
return $pdf;
}
/**
* {@inheritdoc}
*/
public function generateAll(InputInterface $input, OutputInterface $output, $width)
{
$params = $this->daux->getParams();
$data = ['author' => $params['author'], 'title' => $params['title'], 'subject' => $params['tagline']];
$book = new Book($this->daux->tree, $data);
$current = $this->daux->tree->getIndexPage();
while ($current) {
$this->runAction(
"Generating " . $current->getTitle(),
$output,
$width,
function () use ($book, $current, $params) {
$contentType = $this->daux->getContentTypeHandler()->getType($current);
$content = ContentPage::fromFile($current, $params, $contentType)->getContent();
$book->addPage($current, $content);
}
);
$current = $current->getNext();
}
$content = $book->generate();
file_put_contents($input->getOption('destination') . '/file.html', $content);
}
}

74
libs/GeneratorHelper.php Normal file
View File

@ -0,0 +1,74 @@
<?php namespace Todaymade\Daux;
class GeneratorHelper
{
/**
* Copy all files from $path to $local_base
*
* @param string $path
* @param string $local_base
*/
public static function copyAssets($path, $local_base)
{
if (is_dir($path)) {
static::rmdir($path);
} else {
mkdir($path);
}
mkdir($path . DIRECTORY_SEPARATOR . 'themes');
static::copyRecursive(
$local_base . DIRECTORY_SEPARATOR . 'themes',
$path . DIRECTORY_SEPARATOR . 'themes'
);
}
/**
* Remove a directory recursively
*
* @param string $dir
*/
protected static function rmdir($dir)
{
$it = new \RecursiveDirectoryIterator($dir);
$files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $file) {
if ($file->getFilename() === '.' || $file->getFilename() === '..') {
continue;
}
if ($file->isDir()) {
rmdir($file->getRealPath());
} else {
unlink($file->getRealPath());
}
}
}
/**
* Copy files recursively
*
* @param string $source
* @param string $destination
*/
protected static function copyRecursive($source, $destination)
{
if (!is_dir($destination)) {
mkdir($destination);
}
$dir = opendir($source);
while (false !== ($file = readdir($dir))) {
if ($file != '.' && $file != '..') {
if (is_dir($source . DIRECTORY_SEPARATOR . $file)) {
static::copyRecursive(
$source . DIRECTORY_SEPARATOR . $file,
$destination . DIRECTORY_SEPARATOR . $file
);
} else {
copy($source . DIRECTORY_SEPARATOR . $file, $destination . DIRECTORY_SEPARATOR . $file);
}
}
}
closedir($dir);
}
}

83
libs/Processor.php Normal file
View File

@ -0,0 +1,83 @@
<?php namespace Todaymade\Daux;
use League\CommonMark\Environment;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Tree\Root;
class Processor
{
/**
* @var Daux
*/
protected $daux;
/**
* @var OutputInterface
*/
protected $output;
/**
* @var integer
*/
protected $width;
/**
* @param Daux $daux
* @param OutputInterface $output
* @param integer $width
*/
public function __construct(Daux $daux, OutputInterface $output, $width)
{
$this->daux = $daux;
$this->output = $output;
$this->width = $width;
}
/**
* With this connection point, you can transform
* the tree as you want, move pages, modify
* pages and even add new ones.
*
* @param Root $root
*/
public function manipulateTree(Root $root)
{
}
/**
* This connection point provides
* a way to extend the Markdown
* parser and renderer.
*
* @param Environment $environment
*/
public function extendCommonMarkEnvironment(Environment $environment)
{
}
/**
* Provide new generators with this extension point. You
* can simply return an array, the key is the format's
* name, the value is a class name implementing the
* `Todaymade\Daux\Format\Base\Generator` contract.
* You can also replace base generators.
*
* @return string[]
*/
public function addGenerators()
{
return [];
}
/**
* Provide new content Types to be used during the generation
* phase, with this you can change the markdown parser or add
* a completely different file type.
*
* @return \Todaymade\Daux\ContentTypes\ContentType[]
*/
public function addContentType()
{
return [];
}
}

43
libs/Server/ErrorPage.php Normal file
View File

@ -0,0 +1,43 @@
<?php namespace Todaymade\Daux\Server;
use Todaymade\Daux\Format\HTML\SimplePage;
use Todaymade\Daux\Format\HTML\Template;
class ErrorPage extends SimplePage
{
const NORMAL_ERROR_TYPE = 'NORMAL_ERROR';
const MISSING_PAGE_ERROR_TYPE = 'MISSING_PAGE_ERROR';
const FATAL_ERROR_TYPE = 'FATAL_ERROR';
/**
* @var \Todaymade\Daux\Config
*/
private $params;
/**
* @param string $title
* @param string $content
* @param \Todaymade\Daux\Config $params
*/
public function __construct($title, $content, $params)
{
parent::__construct($title, $content);
$this->params = $params;
}
/**
* @return string
*/
protected function generatePage()
{
$params = $this->params;
$page = [
'title' => $this->title,
'content' => $this->content,
'language' => '',
];
$template = new Template($params['templates'], $params['theme']['templates']);
return $template->render('error', ['page' => $page, 'params' => $params]);
}
}

81
libs/Server/MimeType.php Normal file
View File

@ -0,0 +1,81 @@
<?php namespace Todaymade\Daux\Server;
/**
* Class MimeType
* @package Defr
* @author Dennis Fridrich <fridrich.dennis@gmail.com>
* @see http://www.php.net/mime_content_type
*/
class MimeType
{
/**
* @var array
*/
protected static $mimeTypes = array(
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'php' => 'text/html',
'css' => 'text/css',
'js' => 'application/javascript',
'json' => 'application/json',
'xml' => 'application/xml',
'swf' => 'application/x-shockwave-flash',
'flv' => 'video/x-flv',
// Images
'png' => 'image/png',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'ico' => 'image/vnd.microsoft.icon',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',
// Archives
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
'exe' => 'application/x-msdownload',
'msi' => 'application/x-msdownload',
'cab' => 'application/vnd.ms-cab-compressed',
// Audio/video
'mp3' => 'audio/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
// Adobe
'pdf' => 'application/pdf',
'psd' => 'image/vnd.adobe.photoshop',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
// MS Office
'doc' => 'application/msword',
'rtf' => 'application/rtf',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
// Open Office
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
);
/**
* @param $filename
* @return string
*/
public static function get($filename)
{
$pathInfo = pathinfo($filename);
$extension = strtolower($pathInfo['extension']);
if (array_key_exists($extension, self::$mimeTypes)) {
return self::$mimeTypes[$extension];
} elseif (function_exists('finfo_open') && is_file($filename)) {
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $filename);
finfo_close($finfo);
return $mimetype;
} else {
return 'application/octet-stream';
}
}
}

View File

@ -0,0 +1,7 @@
<?php namespace Todaymade\Daux\Server;
use Todaymade\Daux\Exception;
class NotFoundException extends Exception
{
}

183
libs/Server/Server.php Normal file
View File

@ -0,0 +1,183 @@
<?php namespace Todaymade\Daux\Server;
use Symfony\Component\Console\Output\NullOutput;
use Todaymade\Daux\Daux;
use Todaymade\Daux\DauxHelper;
use Todaymade\Daux\Exception;
use Todaymade\Daux\Format\Base\LiveGenerator;
use Todaymade\Daux\Format\HTML\RawPage;
class Server
{
private $daux;
private $params;
private $host;
private $base_url;
/**
* Serve the documentation
*
* @throws Exception
*/
public static function serve()
{
$daux = new Daux(Daux::LIVE_MODE);
$daux->setThemesPath($daux->getParams()['themes_directory']);
$daux->setDocumentationPath($daux->getParams()['docs_directory']);
$daux->initializeConfiguration();
$class = $daux->getProcessorClass();
if (!empty($class)) {
$daux->setProcessor(new $class($daux, new NullOutput(), 0));
}
// Set this critical configuration
// for the tree generation
$daux->getParams()['index_key'] = 'index';
// Improve the tree with a processor
$daux->generateTree();
$server = new static($daux);
try {
$page = $server->handle($_REQUEST);
} catch (NotFoundException $e) {
http_response_code(404);
$page = new ErrorPage("An error occured", $e->getMessage(), $daux->getParams());
}
if ($page instanceof RawPage) {
header('Content-type: ' . MimeType::get($page->getFile()));
// Transfer file in 1024 byte chunks to save memory usage.
if ($fd = fopen($page->getFile(), 'rb')) {
while (!feof($fd)) {
print fread($fd, 1024);
}
fclose($fd);
}
return;
}
header('Content-type: text/html; charset=utf-8');
echo $page->getContent();
}
public function __construct(Daux $daux)
{
$this->daux = $daux;
$this->host = $_SERVER['HTTP_HOST'];
// The path has a special treatment on windows, revert the slashes
$dir = dirname($_SERVER['PHP_SELF']);
$this->base_url = $_SERVER['HTTP_HOST'] . (DIRECTORY_SEPARATOR == "\\"? str_replace($dir, "\\", "/") : $dir);
$t = strrpos($this->base_url, '/index.php');
if ($t != false) {
$this->base_url = substr($this->base_url, 0, $t);
}
if (substr($this->base_url, -1) !== '/') {
$this->base_url .= '/';
}
}
/**
* @return \Todaymade\Daux\Config
*/
public function getParams()
{
$params = $this->daux->getParams();
$params['host'] = $this->host;
DauxHelper::rebaseConfiguration($params, '//' . $this->base_url);
$params['base_page'] = '//' . $this->base_url;
if (!$this->daux->options['live']['clean_urls']) {
$params['base_page'] .= 'index.php/';
}
return $params;
}
/**
* Handle an incoming request
*
* @param array $query
* @return \Todaymade\Daux\Format\Base\Page
* @throws Exception
* @throws NotFoundException
*/
public function handle($query = [])
{
$this->params = $this->getParams();
$request = $this->getRequest();
$request = urldecode($request);
if ($request == 'index_page') {
$request = $this->daux->tree->getIndexPage()->getUri();
}
return $this->getPage($request);
}
/**
* @param string $request
* @return \Todaymade\Daux\Format\Base\Page
* @throws NotFoundException
*/
private function getPage($request)
{
$file = DauxHelper::getFile($this->daux->tree, $request);
if ($file === false) {
throw new NotFoundException('The Page you requested is yet to be made. Try again later.');
}
$generator = $this->daux->getGenerator();
if (!$generator instanceof LiveGenerator) {
throw new \RuntimeException(
"The generator '" . get_class($generator) . "' does not implement the interface " .
"'Todaymade\\Daux\\Format\\Base\\LiveGenerator' and thus doesn't support live rendering."
);
}
return $this->daux->getGenerator()->generateOne($file, $this->params);
}
public function getRequest()
{
if (isset($_SERVER['PATH_INFO'])) {
$uri = $_SERVER['PATH_INFO'];
} elseif (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
$uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
} elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) {
$uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
}
if (strncmp($uri, '?/', 2) === 0) {
$uri = substr($uri, 2);
}
$parts = preg_split('#\?#i', $uri, 2);
$uri = $parts[0];
if (isset($parts[1])) {
$_SERVER['QUERY_STRING'] = $parts[1];
parse_str($_SERVER['QUERY_STRING'], $_GET);
} else {
$_SERVER['QUERY_STRING'] = '';
$_GET = array();
}
$uri = parse_url($uri, PHP_URL_PATH);
} else {
return false;
}
$uri = str_replace(array('//', '../'), '/', trim($uri, '/'));
if ($uri == "") {
$uri = "index_page";
}
return $uri;
}
}

202
libs/Tree/Builder.php Normal file
View File

@ -0,0 +1,202 @@
<?php namespace Todaymade\Daux\Tree;
use RuntimeException;
use SplFileInfo;
use Todaymade\Daux\Daux;
use Todaymade\Daux\DauxHelper;
class Builder
{
protected static $IGNORED = [
// Popular VCS Systems
'.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg',
// Operating system files
'.DS_Store', 'Thumbs.db',
];
protected static function isIgnored(\SplFileInfo $file, $ignore)
{
if (in_array($file->getFilename(), static::$IGNORED)) {
return true;
}
if ($file->isDir() && in_array($file->getFilename(), $ignore['folders'])) {
return true;
}
if (!$file->isDir() && in_array($file->getFilename(), $ignore['files'])) {
return true;
}
return false;
}
/**
* Get name for a file
*
* @param string $path
* @return string
*/
protected static function getName($path)
{
// ['dir' => 1, 'basename' => 2, 'filename' => 3, 'extension' => 5]
preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
if (!isset($m[3])) {
throw new RuntimeException("Name not found");
}
return $m[3];
}
/**
* Build the initial tree
*
* @param Directory $node
* @param array $ignore
*/
public static function build($node, $ignore)
{
if (($it = new \FilesystemIterator($node->getPath())) == false) {
return;
}
if ($node instanceof Root) {
// Ignore config.json in the root directory
$ignore['files'][] = 'config.json';
}
/** @var \SplFileInfo $file */
foreach ($it as $file) {
if (static::isIgnored($file, $ignore)) {
continue;
}
if ($file->isDir()) {
$new = new Directory($node, static::removeSortingInformations($file->getFilename()), $file);
$new->setName(static::getName($file->getPathName()));
$new->setTitle(str_replace('_', ' ', static::removeSortingInformations($new->getName())));
static::build($new, $ignore);
} else {
static::createContent($node, $file);
}
}
$node->sort();
}
/**
* @param Directory $parent
* @param SplFileInfo $file
* @return Content|Raw
*/
public static function createContent(Directory $parent, SplFileInfo $file)
{
$name = static::getName($file->getPathname());
$config = $parent->getConfig();
if (!in_array($file->getExtension(), $config['valid_content_extensions'])) {
$uri = static::removeSortingInformations($file->getFilename());
$entry = new Raw($parent, $uri, $file);
$entry->setTitle(str_replace('_', ' ', static::removeSortingInformations($name)));
$entry->setName($name);
return $entry;
}
$uri = static::removeSortingInformations($name);
if ($config['mode'] === Daux::STATIC_MODE) {
$uri .= '.html';
}
$entry = new Content($parent, $uri, $file);
if ($entry->getUri() == $config['index_key']) {
if ($parent instanceof Root) {
$entry->setTitle($config['title']);
} else {
$entry->setTitle($parent->getTitle());
}
} else {
$entry->setTitle(str_replace('_', ' ', static::removeSortingInformations($name)));
}
$entry->setName($name);
return $entry;
}
/**
* @param string $filename
* @return string
*/
public static function removeSortingInformations($filename)
{
preg_match("/^-?[0-9]*_?(.*)/", $filename, $matches);
// Remove the numeric part
// of the filename, only if
// there is something after
return empty($matches[1])? $matches[0] : $matches[1];
}
/**
* @param Directory $parent
* @param String $title
* @return Directory
*/
public static function getOrCreateDir(Directory $parent, $title)
{
$slug = DauxHelper::slug($title);
if (array_key_exists($slug, $parent->getEntries())) {
return $parent->getEntries()[$slug];
}
$dir = new Directory($parent, $slug);
$dir->setTitle($title);
return $dir;
}
/**
* @param Directory $parent
* @param string $path
* @return Content
*/
public static function getOrCreatePage(Directory $parent, $path)
{
$title = static::getName($path);
// If the file doesn't have an extension, set .md as a default
if (pathinfo($path, PATHINFO_EXTENSION) == '') {
$path .= '.md';
}
$uri = $slug = DauxHelper::slug($title);
if ($parent->getConfig()['mode'] === Daux::STATIC_MODE) {
$uri = $slug . ".html";
}
if (array_key_exists($uri, $parent->getEntries())) {
return $parent->getEntries()[$uri];
}
$page = new Content($parent, $uri);
$page->setContent("-"); //set an almost empty content to avoid problems
if ($title == 'index') {
// TODO :: clarify the difference between 'index' and '_index'
$page->setName('_index' . pathinfo($path, PATHINFO_EXTENSION));
$page->setTitle($parent->getTitle());
} else {
$page->setName($path);
$page->setTitle($title);
}
return $page;
}
}

149
libs/Tree/Content.php Normal file
View File

@ -0,0 +1,149 @@
<?php namespace Todaymade\Daux\Tree;
class Content extends Entry
{
/** @var string */
protected $content;
/** @var Content */
protected $previous;
/** @var Content */
protected $next;
/** @var array */
protected $attributes;
/**
* @return string
*/
public function getContent()
{
if (!$this->content) {
$this->content = file_get_contents($this->getPath());
}
if ($this->attributes === null) {
$this->parseAttributes();
}
return $this->content;
}
/**
* @param string $content
*/
public function setContent($content)
{
$this->content = $content;
}
/**
* @return Content
*/
public function getPrevious()
{
return $this->previous;
}
/**
* @param Content $previous
*/
public function setPrevious($previous)
{
$this->previous = $previous;
}
/**
* @return Content
*/
public function getNext()
{
return $this->next;
}
/**
* @param Content $next
*/
public function setNext($next)
{
$this->next = $next;
}
public function isIndex()
{
return $this->name == 'index' || $this->name == '_index';
}
public function getTitle()
{
if ($title = $this->getAttribute('title')) {
return $title;
}
return parent::getTitle();
}
protected function parseAttributes()
{
// We set an empty array first to
// avoid a loop when "parseAttributes"
// is called in "getContent"
$this->attributes = [];
$content = $this->getContent();
$sections = preg_split('/\s+-{3,}\s+/', $content, 2);
// Only do it if we have two sections
if (count($sections) != 2) {
return;
}
// Parse the different attributes
$lines = preg_split('/\n/', $sections[0]);
foreach ($lines as $line) {
$parts = preg_split('/:/', $line, 2);
if (count($parts) !== 2) continue;
$key = strtolower(trim($parts[0]));
$value = trim($parts[1]);
$this->attributes[$key] = $value;
}
// Only remove the content if we have at least one attribute
if (count($this->attributes) > 0) {
$this->setContent($sections[1]);
}
}
public function setAttributes(array $attributes)
{
$this->attributes = $attributes;
}
public function getAttribute($key = null)
{
if ($this->attributes === null) {
$this->parseAttributes();
}
if (is_null($key)) {
return $this->attributes;
}
if (!array_key_exists($key, $this->attributes)) {
return null;
}
return $this->attributes[$key];
}
public function dump()
{
$dump = parent::dump();
$dump['prev'] = $this->getPrevious() ? $this->getPrevious()->getUrl() : '';
$dump['next'] = $this->getNext() ? $this->getNext()->getUrl() : '';
return $dump;
}
}

234
libs/Tree/Directory.php Normal file
View File

@ -0,0 +1,234 @@
<?php namespace Todaymade\Daux\Tree;
use Todaymade\Daux\Daux;
class Directory extends Entry
{
/** @var Entry[] */
protected $children = [];
/** @var Content */
protected $first_page;
public function sort()
{
// Separate the values into buckets to sort them separately
$buckets = [
'index' => [],
'numeric' => [],
'normal' => [],
'down_numeric' => [],
'down' => [],
];
foreach ($this->children as $key => $entry) {
$name = $entry->getName();
if ($name == 'index' || $name == '_index') {
$buckets['index'][$key] = $entry;
continue;
}
if ($name[0] == "-") {
if (is_numeric($name[1])) {
$exploded = explode("_", $name);
$buckets['down_numeric'][abs(substr($exploded[0], 1))][$key] = $entry;
continue;
}
$buckets['down'][$key] = $entry;
continue;
}
if (is_numeric($name[0])) {
$exploded = explode("_", $name);
$buckets['numeric'][abs($exploded[0])][$key] = $entry;
continue;
}
$buckets['normal'][$key] = $entry;
}
$final = [];
foreach ($buckets as $name => $bucket) {
if ($name == 'numeric' || $name == 'down_numeric') {
ksort($bucket);
foreach ($bucket as $sub_bucket) {
$final = $this->sortBucket($sub_bucket, $final);
}
} else {
$final = $this->sortBucket($bucket, $final);
}
}
$this->children = $final;
}
private function sortBucket($bucket, $final)
{
uasort($bucket, function(Entry $a, Entry $b) {
return strcasecmp($a->getName(), $b->getName());
});
foreach ($bucket as $key => $value) {
$final[$key] = $value;
}
return $final;
}
/**
* @return Entry[]
*/
public function getEntries()
{
return $this->children;
}
public function addChild(Entry $entry)
{
$this->children[$entry->getUri()] = $entry;
}
public function removeChild(Entry $entry)
{
unset($this->children[$entry->getUri()]);
}
/**
* @return \Todaymade\Daux\Config
*/
public function getConfig()
{
if (!$this->parent) {
throw new \RuntimeException("Could not retrieve configuration. Are you sure that your tree has a Root ?");
}
return $this->parent->getConfig();
}
/**
* @return Content|null
*/
public function getIndexPage()
{
$index_key = $this->getConfig()['index_key'];
if (isset($this->children[$index_key])) {
return $this->children[$index_key];
}
/*
If the inherit_index flag is set, then we seek child content
*/
if ($this->getConfig()['mode'] == Daux::LIVE_MODE
&& !empty($this->getConfig()['live']['inherit_index'])
&& $first_page = $this->seekFirstPage()
) {
return $first_page;
}
return null;
}
/**
* Seek the first available page from descendants
* @return Content|null
*/
public function seekFirstPage()
{
if ($this instanceof Directory) {
$index_key = $this->getConfig()['index_key'];
if (isset($this->children[$index_key])) {
return $this->children[$index_key];
}
foreach ($this->children as $node_key => $node) {
if ($node instanceof Content) {
return $node;
}
if ($node instanceof Directory
&& strpos($node->getUri(), '.') !== 0
&& $childNode = $node->seekFirstPage() ) {
return $childNode;
}
}
}
return null;
}
/**
* @return Content|null
*/
public function getFirstPage()
{
if ($this->first_page) {
return $this->first_page;
}
// First we try to find a real page
foreach ($this->getEntries() as $node) {
if ($node instanceof Content) {
if ($this instanceof Root && $this->getIndexPage() == $node) {
// The homepage should not count as first page
continue;
}
$this->setFirstPage($node);
return $node;
}
}
// If we can't find one we check in the sub-directories
foreach ($this->getEntries() as $node) {
if ($node instanceof Directory && $page = $node->getFirstPage()) {
$this->setFirstPage($page);
return $page;
}
}
return null;
}
/**
* @param Content $first_page
*/
public function setFirstPage($first_page)
{
$this->first_page = $first_page;
}
/**
* Used when creating the navigation.
* Hides folders without showable content
*
* @return bool
*/
public function hasContent()
{
foreach ($this->getEntries() as $node) {
if ($node instanceof Content) {
return true;
} elseif ($node instanceof Directory) {
if ($node->hasContent()) {
return true;
}
}
}
return false;
}
public function dump()
{
$dump = parent::dump();
$dump['index'] = $this->getIndexPage() ? $this->getIndexPage()->getUrl() : '';
$dump['first'] = $this->getFirstPage() ? $this->getFirstPage()->getUrl() : '';
foreach ($this->getEntries() as $entry) {
$dump['children'][] = $entry->dump();
}
return $dump;
}
}

176
libs/Tree/Entry.php Normal file
View File

@ -0,0 +1,176 @@
<?php namespace Todaymade\Daux\Tree;
use SplFileInfo;
abstract class Entry
{
/** @var string */
protected $title;
/** @var string */
protected $name;
/** @var string */
protected $uri;
/** @var Directory */
protected $parent;
/** @var SplFileInfo */
protected $info;
/** @var string */
protected $path;
/**
* @param Directory $parent
* @param string $uri
* @param SplFileInfo $info
*/
public function __construct(Directory $parent, $uri, SplFileInfo $info = null)
{
$this->setUri($uri);
$this->setParent($parent);
if ($info !== null) {
$this->info = $info;
$this->path = $info->getPathname();
}
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
/**
* @param string $uri
*/
public function setUri($uri)
{
if ($this->parent) {
$this->parent->removeChild($this);
}
$this->uri = $uri;
if ($this->parent) {
$this->parent->addChild($this);
}
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* @return Directory
*/
public function getParent()
{
return $this->parent;
}
/**
* Return all parents starting with the root
*
* @return Directory[]
*/
public function getParents()
{
$parents = [];
if ($this->parent && !$this->parent instanceof Root) {
$parents = $this->parent->getParents();
$parents[] = $this->parent;
}
return $parents;
}
/**
* @param Directory $parent
*/
protected function setParent(Directory $parent)
{
if ($this->parent) {
$this->parent->removeChild($this);
}
$parent->addChild($this);
$this->parent = $parent;
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* @return SplFileInfo
*/
public function getFileinfo()
{
return $this->info;
}
/**
* @return string
*/
public function getUrl()
{
$url = '';
if ($this->getParent() && !$this->getParent() instanceof Root) {
$url = $this->getParent()->getUrl() . '/' . $url;
}
$url .= $this->getUri();
return $url;
}
public function dump()
{
return [
'title' => $this->getTitle(),
'type' => get_class($this),
'name' => $this->getName(),
'uri' => $this->getUri(),
'url' => $this->getUrl(),
'path' => $this->path
];
}
}

5
libs/Tree/Raw.php Normal file
View File

@ -0,0 +1,5 @@
<?php namespace Todaymade\Daux\Tree;
class Raw extends Entry
{
}

38
libs/Tree/Root.php Normal file
View File

@ -0,0 +1,38 @@
<?php namespace Todaymade\Daux\Tree;
use Todaymade\Daux\Config;
class Root extends Directory
{
/** @var Config */
protected $config;
/**
* The root doesn't have a parent
*
* @param string $uri
*/
public function __construct(Config $config, $uri)
{
$this->setConfig($config);
$this->setUri($uri);
$this->path = $uri;
}
/**
* @return Config
*/
public function getConfig()
{
return $this->config;
}
/**
* @param Config $config
*/
public function setConfig($config)
{
$this->config = $config;
}
}

View File

@ -1,341 +0,0 @@
<?php
namespace Todaymade\Daux;
require_once(dirname(__FILE__) . '/../vendor/autoload.php');
require_once('daux_directory.php');
require_once('daux_helper.php');
require_once('daux_page.php');
class Daux
{
const STATIC_MODE = 'DAUX_STATIC';
const LIVE_MODE = 'DAUX_LIVE';
public static $VALID_MARKDOWN_EXTENSIONS;
private $local_base;
private $base_url;
private $host;
private $docs_path;
private $tree;
private $options;
private $error_page;
private $error = false;
private $params;
private $mode;
function __construct($global_config_file = NULL) {
$this->initial_setup($global_config_file);
}
public function initialize($config_file = 'config.json') {
if ($this->error) return;
$this->load_docs_config($config_file);
$this->generate_directory_tree();
if (!$this->error) $this->params = $this->get_page_params();
}
public function generate_static($output_dir = NULL) {
if (is_null($output_dir)) $output_dir = $this->local_base . DIRECTORY_SEPARATOR . 'static';
DauxHelper::clean_copy_assets($output_dir, $this->local_base);
$this->recursive_generate_static($this->tree, $output_dir, $this->params);
}
public function handle_request($url, $query = array()) {
if ($this->error) return $this->error_page;
if (!$this->params['clean_urls']) $this->params['base_page'] .= 'index.php/';
$request = DauxHelper::get_request();
$request = urldecode($request);
$request_type = isset($query['method']) ? $query['method'] : '';
if($request == 'first_page') {
$request = $this->tree->first_page->uri;
}
switch ($request_type) {
case 'DauxEdit':
if ($this->options['file_editor']) {
$content = isset($query['markdown']) ? $query['markdown'] : '';
return $this->save_file($request, $content);
}
return $this->generate_error_page('Editing Disabled', 'Editing is currently disabled in config',
ErrorPage::FATAL_ERROR_TYPE);
default:
return $this->get_page($request);
}
}
private function initial_setup($global_config_file) {
$this->setup_environment_variables();
$this->load_global_config($global_config_file);
}
private function setup_environment_variables() {
global $argc;
$this->local_base = dirname(dirname(__FILE__));
$this->base_url = '';
if (isset($argc)) {
$this->mode = Daux::STATIC_MODE;
return;
}
$this->mode = Daux::LIVE_MODE;
$this->host = $_SERVER['HTTP_HOST'];
$this->base_url = $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']);
$t = strrpos($this->base_url, '/index.php');
if ($t != FALSE) $this->base_url = substr($this->base_url, 0, $t);
if (substr($this->base_url, -1) !== '/') $this->base_url .= '/';
}
private function load_global_config($global_config_file) {
if (is_null($global_config_file)) $global_config_file = $this->local_base . DIRECTORY_SEPARATOR . 'global.json';
if (!file_exists($global_config_file)) {
$this->generate_error_page('Global Config File Missing',
'The Global Config file is missing. Requested File : ' . $global_config_file, ErrorPage::FATAL_ERROR_TYPE);
return;
}
$global_config = json_decode(file_get_contents($global_config_file), true);
if (!isset($global_config)) {
$this->generate_error_page('Corrupt Global Config File',
'The Global Config file is corrupt. Check that the JSON encoding is correct', ErrorPage::FATAL_ERROR_TYPE);
return;
}
if (!isset($global_config['docs_directory'])) {
$this->generate_error_page('Docs Directory not set', 'The Global Config file does not have the docs directory set.',
ErrorPage::FATAL_ERROR_TYPE);
return;
}
$this->docs_path = $global_config['docs_directory'];
if (!is_dir($this->docs_path) && !is_dir($this->docs_path = $this->local_base . DIRECTORY_SEPARATOR . $this->docs_path)) {
$this->generate_error_page('Docs Directory not found',
'The Docs directory does not exist. Check the path again : ' . $this->docs_path, ErrorPage::FATAL_ERROR_TYPE);
return;
}
if (!isset($global_config['valid_markdown_extensions'])) static::$VALID_MARKDOWN_EXTENSIONS = array('md', 'markdown');
else static::$VALID_MARKDOWN_EXTENSIONS = $global_config['valid_markdown_extensions'];
}
private function load_docs_config($config_file) {
$config_file = $this->docs_path . DIRECTORY_SEPARATOR . $config_file;
if (!file_exists($config_file)) {
$this->generate_error_page('Config File Missing',
'The local config file is missing. Check path : ' . $config_file, ErrorPage::FATAL_ERROR_TYPE);
return;
}
$this->options = json_decode(file_get_contents($this->local_base . DIRECTORY_SEPARATOR . 'default.json'), true);
if (is_file($config_file)) {
$config = json_decode(file_get_contents($config_file), true);
if (!isset($config)) {
$this->generate_error_page('Invalid Config File',
'There was an error parsing the Config file. Please review', ErrorPage::FATAL_ERROR_TYPE);
return;
}
$this->options = array_merge($this->options, $config);
}
if (isset($this->options['timezone'])) date_default_timezone_set($this->options['timezone']);
else if (!ini_get('date.timezone')) date_default_timezone_set('GMT');
}
private function generate_directory_tree() {
$this->tree = DauxHelper::build_directory_tree($this->docs_path, $this->options['ignore'], $this->mode);
if (!empty($this->options['languages'])) {
foreach ($this->options['languages'] as $key => $node) {
$this->tree->value[$key]->title = $node;
}
}
}
private function recursive_generate_static($tree, $output_dir, $params, $base_url = '') {
$params['base_url'] = $params['base_page'] = $base_url;
$new_params = $params;
//changed this as well in order for the templates to be put in the right place
$params['theme'] = DauxHelper::rebase_theme($params['theme'], $base_url, $params['base_url'] . "templates/default/themes/" . $params['theme']['name'] . '/');
//
$params['image'] = str_replace('<base_url>', $base_url, $params['image']);
if ($base_url !== '') $params['entry_page'] = $tree->first_page;
foreach ($tree->value as $key => $node) {
if ($node->type === Directory_Entry::DIRECTORY_TYPE) {
$new_output_dir = $output_dir . DIRECTORY_SEPARATOR . $key;
@mkdir($new_output_dir);
$this->recursive_generate_static($node, $new_output_dir, $new_params, '../' . $base_url);
} else {
$params['request'] = $node->get_url();
$params['file_uri'] = $node->name;
$page = MarkdownPage::fromFile($node, $params);
file_put_contents($output_dir . DIRECTORY_SEPARATOR . $key, $page->get_page_content());
}
}
}
private function save_file($request, $content) {
$file = $this->get_file_from_request($request);
if ($file === false) return $this->generate_error_page('Page Not Found',
'The Page you requested is yet to be made. Try again later.', ErrorPage::MISSING_PAGE_ERROR_TYPE);
if ($file->write($content)) return new SimplePage('Success', 'Successfully Edited');
else return $this->generate_error_page('File Not Writable', 'The file you wish to write to is not writable.',
ErrorPage::FATAL_ERROR_TYPE);
}
private function generate_error_page($title, $content, $type) {
$this->error_page = new ErrorPage($title, $content, $this->get_page_params($type));
$this->error = true;
return $this->error_page;
}
private function get_page($request) {
$params = $this->params;
$file = $this->get_file_from_request($request);
if ($file === false) return $this->generate_error_page('Page Not Found',
'The Page you requested is yet to be made. Try again later.', ErrorPage::MISSING_PAGE_ERROR_TYPE);
$params['request'] = $request;
$params['file_uri'] = $file->value;
if ($request !== 'index') $params['entry_page'] = $file->first_page;
return MarkdownPage::fromFile($file, $params);
}
private function get_page_params($mode = '') {
$params = array();
$params['local_base'] = $this->local_base;
if ($mode === '') $mode = $this->mode;
$params['mode'] = $mode;
switch ($mode) {
case ErrorPage::FATAL_ERROR_TYPE:
$params['error_type'] = ErrorPage::FATAL_ERROR_TYPE;
break;
case ErrorPage::NORMAL_ERROR_TYPE:
case ErrorPage::MISSING_PAGE_ERROR_TYPE:
$params['error_type'] = $mode;
$params['index_key'] = 'index';
$params['docs_path'] = $this->docs_path;
$protocol = '//';
$params['base_url'] = $protocol . $this->base_url;
$params['base_page'] = $params['base_url'];
$params['host'] = $this->host;
$params['tree'] = $this->tree;
$params['index'] = ($this->tree->index_page !== false) ? $this->tree->index_page : $this->tree->first_page;
$params['clean_urls'] = $this->options['clean_urls'];
$params['tagline'] = $this->options['tagline'];
$params['title'] = $this->options['title'];
$params['author'] = $this->options['author'];
$params['image'] = $this->options['image'];
if ($params['image'] !== '') $params['image'] = str_replace('<base_url>', $params['base_url'], $params['image']);
$params['repo'] = $this->options['repo'];
$params['links'] = $this->options['links'];
$params['twitter'] = $this->options['twitter'];
$params['google_analytics'] = ($g = $this->options['google_analytics']) ?
DauxHelper::google_analytics($g, $this->host) : '';
$params['piwik_analytics'] = ($p = $this->options['piwik_analytics']) ?
DauxHelper::piwik_analytics($p, $this->options['piwik_analytics_id']) : '';
$params['template'] = $this->options['template'];
$params['theme'] = DauxHelper::configure_theme($this->local_base . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR .
$this->options['template'] . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $this->options['theme'] . '.thm', $params['base_url'],
$this->local_base, $params['base_url'] . "templates/" . $params['template'] . "/themes/" . $this->options['theme'] . '/');
break;
case Daux::LIVE_MODE:
$params['docs_path'] = $this->docs_path;
$params['index_key'] = 'index';
$protocol = '//';
$params['base_url'] = $protocol . $this->base_url;
$params['base_page'] = $params['base_url'];
$params['host'] = $this->host;
$params['tree'] = $this->tree;
$params['index'] = ($this->tree->index_page !== false) ? $this->tree->index_page : $this->tree->first_page;
$params['clean_urls'] = $this->options['clean_urls'];
$params['tagline'] = $this->options['tagline'];
$params['title'] = $this->options['title'];
$params['author'] = $this->options['author'];
$params['image'] = $this->options['image'];
if ($params['image'] !== '') $params['image'] = str_replace('<base_url>', $params['base_url'], $params['image']);
$params['repo'] = $this->options['repo'];
$params['links'] = $this->options['links'];
$params['twitter'] = $this->options['twitter'];
$params['google_analytics'] = ($g = $this->options['google_analytics']) ?
DauxHelper::google_analytics($g, $this->host) : '';
$params['piwik_analytics'] = ($p = $this->options['piwik_analytics']) ?
DauxHelper::piwik_analytics($p, $this->options['piwik_analytics_id']) : '';
$params['template'] = $this->options['template'];
$params['theme'] = DauxHelper::configure_theme($this->local_base . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR .
$this->options['template'] . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $this->options['theme'] . '.thm', $params['base_url'],
$this->local_base, $params['base_url'] . "templates/" . $params['template'] . "/themes/" . $this->options['theme'] . '/', $mode);
if ($params['breadcrumbs'] = $this->options['breadcrumbs'])
$params['breadcrumb_separator'] = $this->options['breadcrumb_separator'];
$params['multilanguage'] = !empty($this->options['languages']);
$params['languages'] = $this->options['languages'];
if (empty($this->options['languages'])) {
$params['entry_page'] = $this->tree->first_page;
} else {
foreach ($this->options['languages'] as $key => $name) {
$params['entry_page'][$key] = $this->tree->value[$key]->first_page;
}
}
$params['toggle_code'] = $this->options['toggle_code'];
$params['float'] = $this->options['float'];
$params['date_modified'] = $this->options['date_modified'];
$params['file_editor'] = $this->options['file_editor'];
break;
case Daux::STATIC_MODE:
$params['docs_path'] = $this->docs_path;
$params['index_key'] = 'index.html';
$params['base_url'] = '';
$params['base_page'] = $params['base_url'];
$params['tree'] = $this->tree;
$params['index'] = ($this->tree->index_page !== false) ? $this->tree->index_page : $this->tree->first_page;
$params['tagline'] = $this->options['tagline'];
$params['title'] = $this->options['title'];
$params['author'] = $this->options['author'];
$params['image'] = $this->options['image'];
$params['repo'] = $this->options['repo'];
$params['links'] = $this->options['links'];
$params['twitter'] = $this->options['twitter'];
$params['google_analytics'] = ($g = $this->options['google_analytics']) ?
DauxHelper::google_analytics($g, $this->host) : '';
$params['piwik_analytics'] = ($p = $this->options['piwik_analytics']) ?
DauxHelper::piwik_analytics($p, $this->options['piwik_analytics_id']) : '';
$params['template'] = $this->options['template'];
$params['theme'] = DauxHelper::configure_theme($this->local_base . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR .
$this->options['template'] . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $this->options['theme'] . '.thm', $params['base_url'],
$this->local_base, $params['base_url'] . "templates/" . $params['template'] . "/themes/" . $this->options['theme'] . '/', $mode);
if ($params['breadcrumbs'] = $this->options['breadcrumbs'])
$params['breadcrumb_separator'] = $this->options['breadcrumb_separator'];
$params['multilanguage'] = !empty($this->options['languages']);
$params['languages'] = $this->options['languages'];
if (empty($this->options['languages'])) {
$params['entry_page'] = $this->tree->first_page;
} else {
foreach ($this->options['languages'] as $key => $name) {
$params['entry_page'][$key] = $this->tree->value[$key]->first_page;
}
}
$params['toggle_code'] = $this->options['toggle_code'];
$params['float'] = $this->options['float'];
$params['date_modified'] = $this->options['date_modified'];
$params['file_editor'] = false;
break;
}
return $params;
}
private function get_file_from_request($request) {
$file = $this->tree->retrieve_file($request);
return $file;
}
}
?>

View File

@ -1,128 +0,0 @@
<?php
namespace Todaymade\Daux;
class Directory_Entry
{
const FILE_TYPE = 'FILE_TYPE';
const DIRECTORY_TYPE = 'DIRECTORY_TYPE';
public $name;
public $title;
public $type;
public $index_page;
public $first_page;
public $value;
public $uri;
public $local_path;
public $last_modified;
public $parents;
function __construct($path = '', $parents = array()) {
if (!isset($path) || $path == '' || !file_exists($path)) return;
$this->local_path = $path;
$this->parents = $parents;
$this->last_modified = filemtime($path);
$this->name = DauxHelper::pathinfo($path);
$this->name = $this->name['filename'];
$this->title = DauxHelper::get_title_from_file($this->name);
$this->uri = DauxHelper::get_url_from_filename($this->name);
$this->index_page = false;
if (is_dir($path)) {
$this->type = Directory_Entry::DIRECTORY_TYPE;
$this->value = array();
} else {
$this->type = Directory_Entry::FILE_TYPE;
$this->value = $this->uri;
}
}
public function sort() {
if ($this->type == static::DIRECTORY_TYPE) uasort($this->value, array($this, 'compare_directory_entries'));
}
public function retrieve_file($request, $get_first_file = false) {
$tree = $this;
$request = explode('/', $request);
foreach ($request as $node) {
if ($tree->type === static::DIRECTORY_TYPE) {
if (isset($tree->value[$node])) $tree = $tree->value[$node];
else {
if ($node === 'index' || $node === 'index.html') {
if ($get_first_file) {
return ($tree->index_page) ? $tree->index_page : $tree->first_page;
} else {
return $tree->index_page;
}
} else return false;
}
} else return false;
}
if ($tree->type === static::DIRECTORY_TYPE) {
if ($tree->index_page) return $tree->index_page;
else return ($get_first_file) ? $tree->first_page : false;
} else {
return $tree;
}
}
public function get_url() {
$url = '';
foreach ($this->parents as $node) {
$url .= $node->uri . '/';
}
$url .= $this->uri;
return $url;
}
public function get_first_page() {
foreach ($this->value as $node) {
if ($node->type === static::FILE_TYPE && $node->title != 'index')
return $node;
}
foreach ($this->value as $node) {
if ($node->type === static::DIRECTORY_TYPE) {
$page = $node->get_first_page();
if ($page) return $page;
}
}
return false;
}
public function write($content) {
if (is_writable($this->local_path)) file_put_contents($this->local_path, $content);
else return false;
return true;
}
private function compare_directory_entries($a, $b) {
$name_a = explode('_', $a->name);
$name_b = explode('_', $b->name);
if (is_numeric($name_a[0])) {
$a = intval($name_a[0]);
if (is_numeric($name_b[0])) {
$b = intval($name_b[0]);
if (($a >= 0) == ($b >= 0)) {
$a = abs($a);
$b = abs($b);
if ($a == $b) return (strcasecmp($name_a[1], $name_b[1]));
return ($a > $b) ? 1 : -1;
}
return ($a >= 0) ? -1 : 1;
}
$t = $name_b[0];
if ($t && $t[0] === '-') return -1;
return ($a < 0) ? 1 : -1;
} else {
if (is_numeric($name_b[0])) {
$b = intval($name_b[0]);
if ($b >= 0) return 1;
$t = $name_a[0];
if ($t && $t[0] === '-') return 1;
return ($b >= 0) ? 1 : -1;
}
$p = $name_a[0];
$q = $name_b[0];
if (($p && $p[0] === '-') == ($q && $q[0] === '-')) return strcasecmp($p, $q);
else return ($p[0] === '-') ? 1 : -1;
}
}
}
?>

View File

@ -1,363 +0,0 @@
<?php
namespace Todaymade\Daux;
class DauxHelper {
public static function get_breadcrumb_title_from_request($request, $separator = 'Chevrons', $multilanguage = false) {
if ($multilanguage) $request = substr($request, strpos($request, '/') + 1);
$request = str_replace('_', ' ', $request);
switch ($separator) {
case 'Chevrons':
$request = str_replace('/', ' <i class="glyphicon glyphicon-chevron-right"></i> ', $request);
return $request;
case 'Colons':
$request = str_replace('/', ': ', $request);
return $request;
case 'Spaces':
$request = str_replace('/', ' ', $request);
return $request;
default:
$request = str_replace('/', $separator, $request);
return $request;
}
return $request;
}
public static function get_title_from_file($file) {
$file = static::pathinfo($file);
return static::get_title_from_filename($file['filename']);
}
public static function get_title_from_filename($filename) {
$filename = explode('_', $filename);
if ($filename[0] == '' || is_numeric($filename[0])) unset($filename[0]);
else {
$t = $filename[0];
if ($t[0] == '-') $filename[0] = substr($t, 1);
}
$filename = implode(' ', $filename);
return $filename;
}
public static function get_url_from_file($file) {
$file = static::pathinfo($file);
return static::get_url_from_filename($file['filename']);
}
public static function get_url_from_filename($filename) {
$filename = explode('_', $filename);
if ($filename[0] == '' || is_numeric($filename[0])) unset($filename[0]);
else {
$t = $filename[0];
if ($t[0] == '-') $filename[0] = substr($t, 1);
}
$filename = implode('_', $filename);
return $filename;
}
public static function build_directory_tree($dir, $ignore, $mode) {
return static::directory_tree_builder($dir, $ignore, $mode);
}
//Depreciated
public static function get_request_from_url($url, $base_url) {
$url = substr($url, strlen($base_url));
if (strpos($url, 'index.php') === 0) {
$request = (($i = strpos($url, 'request=')) !== false) ? $request = substr($url, $i + 8) : '';
if ($end = strpos($request, '&')) $request = substr($request, 0, $end);
$request = ($request === '') ? 'index' : $request;
} else {
$request = ($url == '') ? 'index' : $url;
$request = ($end = strpos($request, '?')) ? substr($request, 0, $end) : $request;
}
return $request;
}
public static function get_request($prefix_slash = false)
{
if (isset($_SERVER['PATH_INFO'])) $uri = $_SERVER['PATH_INFO'];
else if (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
else if (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
if (strncmp($uri, '?/', 2) === 0) $uri = substr($uri, 2);
$parts = preg_split('#\?#i', $uri, 2);
$uri = $parts[0];
if (isset($parts[1])) {
$_SERVER['QUERY_STRING'] = $parts[1];
parse_str($_SERVER['QUERY_STRING'], $_GET);
} else {
$_SERVER['QUERY_STRING'] = '';
$_GET = array();
}
$uri = parse_url($uri, PHP_URL_PATH);
}
else return false;
$uri = str_replace(array('//', '../'), '/', trim($uri, '/'));
if ($uri == "") $uri = "first_page";
return $uri;
}
public static function configure_theme($theme, $base_url, $local_base, $theme_url, $mode = Daux::LIVE_MODE) {
$name = static::pathinfo($theme);
if (is_file($theme)) {
$theme = file_get_contents($theme);
$theme = json_decode($theme, true);
if (!$theme) $theme = array();
} else $theme = array();
$theme['name'] = $name['filename'];
if ($mode === Daux::LIVE_MODE) {
if (!isset($theme['favicon'])) $theme['favicon'] = utf8_encode($base_url . 'img/favicon.png');
else {
$theme['favicon'] = utf8_encode(str_replace('<base_url>', $base_url, $theme['favicon']));
$theme['favicon'] = str_replace('<theme_url>', $theme_url, $theme['favicon']);
}
if (!isset($theme['css'])) $theme['css'] = array();
else {
foreach ($theme['css'] as $key => $css) {
$theme['css'][$key] = utf8_encode(str_replace('<base_url>', $base_url, $css));
$theme['css'][$key] = utf8_encode(str_replace('<theme_url>', $theme_url, $css));
}
}
if (!isset($theme['fonts'])) $theme['fonts'] = array();
else {
foreach ($theme['fonts'] as $key => $font) {
$theme['fonts'][$key] = utf8_encode(str_replace('<base_url>', $base_url, $font));
$theme['fonts'][$key] = utf8_encode(str_replace('<theme_url>', $theme_url, $font));
}
}
if (!isset($theme['js'])) $theme['js'] = array();
else {
foreach ($theme['js'] as $key => $js) {
$theme['js'][$key] = utf8_encode(str_replace('<base_url>', $base_url, $js));
$theme['js'][$key] = utf8_encode(str_replace('<theme_url>', $theme_url, $js));
}
}
} else {
if (!isset($theme['favicon'])) $theme['favicon'] = '<base_url>img/favicon.png';
if (!isset($theme['css'])) $theme['css'] = array();
if (!isset($theme['fonts'])) $theme['fonts'] = array();
if (!isset($theme['js'])) $theme['js'] = array();
}
if (!isset($theme['template'])) $theme['template'] = $local_base . DIRECTORY_SEPARATOR . 'templates' .
DIRECTORY_SEPARATOR . 'default/default.tpl';
else $theme['template'] = str_replace('<local_base>', $local_base, $theme['template']);
if (!isset($theme['error-template'])) $theme['error-template'] = $local_base . DIRECTORY_SEPARATOR . 'templates' .
DIRECTORY_SEPARATOR . 'default/error.tpl';
else $theme['error-template'] = str_replace('<local_base>', $local_base, $theme['error-template']);
if (!isset($theme['require-jquery'])) $theme['require-jquery'] = false;
if (!isset($theme['bootstrap-js'])) $theme['bootstrap-js'] = false;
return $theme;
}
public static function rebase_theme($theme, $base_url, $theme_url) {
$theme['favicon'] = utf8_encode(str_replace('<base_url>', $base_url, $theme['favicon']));
$theme['favicon'] = str_replace('<theme_url>', $theme_url, $theme['favicon']);
foreach ($theme['css'] as $key => $css) {
$theme['css'][$key] = utf8_encode(str_replace('<base_url>', $base_url, $css));
$theme['css'][$key] = utf8_encode(str_replace('<theme_url>', $theme_url, $css));
}
foreach ($theme['fonts'] as $key => $font) {
$theme['fonts'][$key] = utf8_encode(str_replace('<base_url>', $base_url, $font));
$theme['fonts'][$key] = utf8_encode(str_replace('<theme_url>', $theme_url, $font));
}
foreach ($theme['js'] as $key => $js) {
$theme['js'][$key] = utf8_encode(str_replace('<base_url>', $base_url, $js));
$theme['js'][$key] = utf8_encode(str_replace('<theme_url>', $theme_url, $js));
}
return $theme;
}
public static function google_analytics($analytics, $host) {
$ga = <<<EOT
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
EOT;
$ga .= "ga('create', '" . $analytics . "', '" . $host . "');";
$ga .= "ga('send', 'pageview');";
$ga .= '</script>';
return $ga;
}
public static function piwik_analytics($analytics_url, $analytics_id) {
$pa = <<<EOT
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]);
(function() {
EOT;
$pa .= 'var u=(("https:" == document.location.protocol) ? "https" : "http") + "://' . $analytics_url . '/";';
$pa .= '_paq.push(["setTrackerUrl", u+"piwik.php"]);';
$pa .= '_paq.push(["setSiteId", ' . $analytics_id . ']);';
$pa .= <<<EOT
var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
})();
</script>
EOT;
return $pa;
}
private static function directory_tree_builder($dir, $ignore, $mode = Daux::LIVE_MODE, $parents = null) {
if ($dh = opendir($dir)) {
$node = new Directory_Entry($dir, $parents);
$new_parents = $parents;
if (is_null($new_parents)) $new_parents = array();
else $new_parents[] = $node;
while (($entry = readdir($dh)) !== false) {
if ($entry == '.' || $entry == '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $entry;
if (is_dir($path) && in_array($entry, $ignore['folders'])) continue;
if (!is_dir($path) && in_array($entry, $ignore['files'])) continue;
$file_details = static::pathinfo($path);
if (is_dir($path)) $entry = static::directory_tree_builder($path, $ignore, $mode, $new_parents);
else if (in_array($file_details['extension'], Daux::$VALID_MARKDOWN_EXTENSIONS))
{
$entry = new Directory_Entry($path, $new_parents);
if ($mode === Daux::STATIC_MODE) $entry->uri .= '.html';
}
if ($entry instanceof Directory_Entry) $node->value[$entry->uri] = $entry;
}
$node->sort();
$node->first_page = $node->get_first_page();
$index_key = ($mode === Daux::LIVE_MODE) ? 'index' : 'index.html';
if (isset($node->value[$index_key])) {
$node->value[$index_key]->first_page = $node->first_page;
$node->index_page = $node->value[$index_key];
} else $node->index_page = false;
return $node;
}
}
public static function pathinfo($path) {
preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
if (isset($m[1])) $ret['dir']=$m[1];
if (isset($m[2])) $ret['basename']=$m[2];
if (isset($m[5])) $ret['extension']=$m[5];
if (isset($m[3])) $ret['filename']=$m[3];
return $ret;
}
public static function clean_copy_assets($path, $local_base){
@mkdir($path);
static::clean_directory($path);
@mkdir($path . DIRECTORY_SEPARATOR . 'img');
static::copy_recursive($local_base . DIRECTORY_SEPARATOR . 'img', $path . DIRECTORY_SEPARATOR . 'img');
@mkdir($path . DIRECTORY_SEPARATOR . 'js');
static::copy_recursive($local_base . DIRECTORY_SEPARATOR . 'js', $path . DIRECTORY_SEPARATOR . 'js');
//added and changed these in order to fetch the theme files and put them in the right place
@mkdir($path . DIRECTORY_SEPARATOR . 'templates');
@mkdir($path . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'default');
@mkdir($path . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'themes');
static::copy_recursive($local_base . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR .
'default' . DIRECTORY_SEPARATOR . 'themes', $path . DIRECTORY_SEPARATOR . 'templates' .
DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'themes');
//
}
// Rmdir
private static function clean_directory($dir) {
$it = new \RecursiveDirectoryIterator($dir);
$files = new \RecursiveIteratorIterator($it,
\RecursiveIteratorIterator::CHILD_FIRST);
foreach($files as $file) {
if ($file->getFilename() === '.' || $file->getFilename() === '..') continue;
if ($file->isDir()) rmdir($file->getRealPath());
else unlink($file->getRealPath());
}
}
private static function copy_recursive($src,$dst) {
$dir = opendir($src);
@mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
static::copy_recursive($src . '/' . $file,$dst . '/' . $file);
}
else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
}
if (!function_exists('http_response_code')) {
function http_response_code($code = NULL) {
if ($code !== NULL) {
switch ($code) {
case 100: $text = 'Continue'; break;
case 101: $text = 'Switching Protocols'; break;
case 200: $text = 'OK'; break;
case 201: $text = 'Created'; break;
case 202: $text = 'Accepted'; break;
case 203: $text = 'Non-Authoritative Information'; break;
case 204: $text = 'No Content'; break;
case 205: $text = 'Reset Content'; break;
case 206: $text = 'Partial Content'; break;
case 300: $text = 'Multiple Choices'; break;
case 301: $text = 'Moved Permanently'; break;
case 302: $text = 'Moved Temporarily'; break;
case 303: $text = 'See Other'; break;
case 304: $text = 'Not Modified'; break;
case 305: $text = 'Use Proxy'; break;
case 400: $text = 'Bad Request'; break;
case 401: $text = 'Unauthorized'; break;
case 402: $text = 'Payment Required'; break;
case 403: $text = 'Forbidden'; break;
case 404: $text = 'Not Found'; break;
case 405: $text = 'Method Not Allowed'; break;
case 406: $text = 'Not Acceptable'; break;
case 407: $text = 'Proxy Authentication Required'; break;
case 408: $text = 'Request Time-out'; break;
case 409: $text = 'Conflict'; break;
case 410: $text = 'Gone'; break;
case 411: $text = 'Length Required'; break;
case 412: $text = 'Precondition Failed'; break;
case 413: $text = 'Request Entity Too Large'; break;
case 414: $text = 'Request-URI Too Large'; break;
case 415: $text = 'Unsupported Media Type'; break;
case 500: $text = 'Internal Server Error'; break;
case 501: $text = 'Not Implemented'; break;
case 502: $text = 'Bad Gateway'; break;
case 503: $text = 'Service Unavailable'; break;
case 504: $text = 'Gateway Time-out'; break;
case 505: $text = 'HTTP Version not supported'; break;
default:
exit('Unknown http status code "' . htmlentities($code) . '"');
break;
}
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header($protocol . ' ' . $code . ' ' . $text);
$GLOBALS['http_response_code'] = $code;
} else {
$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
}
return $code;
}
}
?>

View File

@ -1,200 +0,0 @@
<?php
namespace Todaymade\Daux;
interface Page
{
function get_page_content();
function display();
}
class SimplePage implements Page
{
protected $title;
protected $content;
protected $html = null;
public function __construct($title, $content) {
$this->initialize_page($title, $content);
}
public function initialize_page($title, $content) {
$this->title = $title;
$this->content = $content;
}
public function display() {
header('Content-type: text/html; charset=utf-8');
echo $this->get_page_content();
}
public function get_page_content() {
if (is_null($this->html)) {
$this->html = $this->generate_page();
}
return $this->html;
}
private function generate_page() {
return $this->content;
}
}
class ErrorPage extends SimplePage
{
const NORMAL_ERROR_TYPE = 'NORMAL_ERROR';
const MISSING_PAGE_ERROR_TYPE = 'MISSING_PAGE_ERROR';
const FATAL_ERROR_TYPE = 'FATAL_ERROR';
private $params;
private $type;
private static $template;
public function __construct($title, $content, $params) {
parent::__construct($title, $content);
$this->params = $params;
$this->type = $params['error_type'];
}
public function display() {
http_response_code($this->type === static::MISSING_PAGE_ERROR_TYPE ? 404 : 500);
parent::display();
}
public function get_page_content() {
if ($this->type !== static::FATAL_ERROR_TYPE && is_null(static::$template)) {
include_once($this->params['theme']['error-template']);
static::$template = new Template();
}
if (is_null($this->html)) {
$this->html = $this->generate_page();
}
return $this->html;
}
public function generate_page() {
if ($this->type === static::FATAL_ERROR_TYPE) return $this->content;
$params = $this->params;
$page['title'] = $this->title;
$page['theme'] = $params['theme'];
$page['content'] = $this->content;
$page['google_analytics'] = $params['google_analytics'];
$page['piwik_analytics'] = $params['piwik_analytics'];
return static::$template->get_content($page, $params);
}
}
class MarkdownPage extends SimplePage
{
private $filename;
private $params;
private $language;
private $mtime;
private $homepage;
private $breadcrumb_trail;
private static $template;
public function __construct() {
}
// For Future Expansion
public static function fromFile($file, $params) {
$instance = new self();
$instance->initialize_from_file($file, $params);
return $instance;
}
private function initialize_from_file($file, $params) {
$this->title = $file->title;
$this->filename = $file->name;
$this->path = $file->local_path;
$this->mtime = $file->last_modified;
$this->params = $params;
if ($this->title === 'index') {
$this->homepage = ($this->filename === '_index');
$minimum_parent_dir_size = ($params['multilanguage']) ? 2 : 1;
if (count($file->parents) >= $minimum_parent_dir_size) {
$parent = end($file->parents);
$this->title = $parent->title;
} else $this->title = $params['title'];
} else {
$this->homepage = false;
}
if ($params['breadcrumbs'])
$this->breadcrumb_trail = $this->build_breadcrumb_trail($file->parents, $params['multilanguage']);
$this->language = '';
if ($params['multilanguage'] && !empty($file->parents)) {
reset($file->parents);
$language_dir = current($file->parents);
$this->language = $language_dir->name;
}
if (is_null(static::$template)) {
include_once($params['theme']['template']);
static::$template = new Template();
}
}
private function build_breadcrumb_trail($parents, $multilanguage) {
if ($multilanguage && !empty($parents)) $parents = array_splice($parents, 1);
$breadcrumb_trail = array();
if (!empty($parents)) {
foreach ($parents as $node) {
$breadcrumb_trail[$node->title] = $node->get_url();
}
}
return $breadcrumb_trail;
}
public function get_page_content() {
if (is_null($this->html)) {
$this->content = file_get_contents($this->path);
$this->html = $this->generate_page();
}
return $this->html;
}
private function generate_page() {
$params = $this->params;
$Parsedown = new \Parsedown();
if ($params['request'] === $params['index_key']) {
if ($params['multilanguage']) {
foreach ($params['languages'] as $key => $name) {
$entry_page[utf8_encode($name)] = utf8_encode($params['base_page'] . $params['entry_page'][$key]->get_url());
}
} else $entry_page['View Documentation'] = utf8_encode($params['base_page'] . $params['entry_page']->uri);
} else if ($params['file_uri'] === 'index')
$entry_page[utf8_encode($params['entry_page']->title)] = utf8_encode($params['base_page'].
$params['entry_page']->get_url());
$page['entry_page'] = (isset($entry_page)) ? $entry_page : null;
$page['homepage'] = $this->homepage;
$page['title'] = $this->title;
$page['tagline'] = $params['tagline'];
$page['author'] = $params['author'];
$page['filename'] = $this->filename;
if ($page['breadcrumbs'] = $params['breadcrumbs']) {
$page['breadcrumb_trail'] = $this->breadcrumb_trail;
$page['breadcrumb_separator'] = $params['breadcrumb_separator'];
}
$page['language'] = $this->language;
$page['path'] = $this->path;
$page['request'] = utf8_encode($params['request']);
$page['theme'] = $params['theme'];
$page['modified_time'] = filemtime($this->path);
$page['markdown'] = $this->content;
$page['content'] = $Parsedown->text($this->content);
$page['file_editor'] = $params['file_editor'];
$page['google_analytics'] = $params['google_analytics'];
$page['piwik_analytics'] = $params['piwik_analytics'];
return static::$template->get_content($page, $params);
}
}
?>

View File

@ -2,12 +2,15 @@
"name": "daux.io", "name": "daux.io",
"version": "0.1.1", "version": "0.1.1",
"private": true, "private": true,
"dependencies": { "devDependencies": {
"grunt": "~0.4.1", "grunt": "~0.4.1",
"grunt-php": "~0.3.0", "grunt-php": "~0.3.0",
"grunt-contrib-less": "~0.9.0" "csswring": "^3.0.5",
}, "gulp": "^3.9.0",
"devDependencies": { "gulp-connect-php": "0.0.5",
"grunt-contrib-watch": "~0.5.0" "gulp-less": "^3.0.3",
"gulp-plumber": "^1.0.1",
"gulp-postcss": "^5.1.10",
"gulp-rename": "^1.2.2"
} }
} }

24
phpunit.xml Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Daux Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">libs</directory>
</whitelist>
</filter>
</phpunit>

3
serve Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
php -S 0.0.0.0:8085 index.php

44
templates/content.php Normal file
View File

@ -0,0 +1,44 @@
<?php $this->layout('theme::layout/05_page') ?>
<article>
<?php if ($params['html']['date_modified']) { ?>
<div class="page-header sub-header clearfix">
<h1><?php
if ($page['breadcrumbs']) {
echo $this->get_breadcrumb_title($page, $base_page);
} else {
echo $page['title'];
}
?>
</h1>
<span style="float: left; font-size: 10px; color: gray;">
<?= date("l, F j, Y", $page['modified_time']); ?>
</span>
<span style="float: right; font-size: 10px; color: gray;">
<?= date("g:i A", $page['modified_time']); ?>
</span>
</div>
<?php } else { ?>
<div class="page-header">
<h1><?php
if ($page['breadcrumbs']) {
echo $this->get_breadcrumb_title($page, $base_page);
} else {
echo $page['title'];
}
?>
</h1>
</div>
<?php } ?>
<?= $page['content']; ?>
<?php if(!empty($page['prev']) || !empty($page['next'])) { ?>
<nav>
<ul class="pager">
<?php if(!empty($page['prev'])) { ?><li><a href="<?= $base_url . $page['prev']->getUrl() ?>">Previous</a></li><?php } ?>
<?php if(!empty($page['next'])) { ?><li><a href="<?= $base_url . $page['next']->getUrl() ?>">Next</a></li><?php } ?>
</ul>
</nav>
<?php } ?>
</article>

View File

@ -1,306 +0,0 @@
<?php
namespace Todaymade\Daux;
class Template {
private function get_navigation($tree, $path, $current_url, $base_page, $mode) {
$nav = '<ul class="nav nav-list">';
$nav .= $this->build_navigation($tree, $path, $current_url, $base_page, $mode);
$nav .= '</ul>';
return $nav;
}
private function build_navigation($tree, $path, $current_url, $base_page, $mode) {
$nav = '';
foreach ($tree->value as $node) {
$url = $node->uri;
if ($node->type === \TodayMade\Daux\Directory_Entry::FILE_TYPE) {
if ($node->value === 'index') continue;
$nav .= '<li';
$link = ($path === '') ? $url : $path . '/' . $url;
if ($current_url === $link) $nav .= ' class="active"';
$nav .= '><a href="' . $base_page . $link . '">' . $node->title . '</a></li>';
} else {
$nav .= '<li';
$link = ($path === '') ? $url : $path . '/' . $url;
if (strpos($current_url, $link) === 0) $nav .= ' class="open"';
$nav .= ">";
if ($mode === \TodayMade\Daux\Daux::STATIC_MODE) $link .= "/index.html";
if ($node->index_page) $nav .= '<a href="' . $base_page . $link . '" class="folder">' .
$node->title . '</a>';
else $nav .= '<a href="#" class="aj-nav folder">' . $node->title . '</a>';
$nav .= '<ul class="nav nav-list">';
$new_path = ($path === '') ? $url : $path . '/' . $url;
$nav .= $this->build_navigation($node, $new_path, $current_url, $base_page, $mode);
$nav .= '</ul></li>';
}
}
return $nav;
}
private function get_breadcrumb_title($page, $base_page) {
$title = '';
$breadcrumb_trail = $page['breadcrumb_trail'];
$separator = $this->get_separator($page['breadcrumb_separator']);
foreach ($breadcrumb_trail as $key => $value) {
$title .= '<a href="' . $base_page . $value . '">' . $key . '</a>' . $separator;
}
if ($page['filename'] === 'index' || $page['filename'] === '_index') {
if ($page['title'] != '') $title = substr($title, 0, -1 * strlen($separator));
} else $title .= '<a href="' . $base_page . $page['request'] . '">' . $page['title'] . '</a>';
return $title;
}
private function get_separator($separator) {
switch ($separator) {
case 'Chevrons':
return ' <i class="glyphicon glyphicon-chevron-right"></i> ';
default:
return $separator;
}
}
public function get_content($page, $params) {
$base_url = $params['base_url'];
$base_page = $params['base_page'];
$homepage = $page['homepage'];
$project_title = utf8_encode($params['title']);
$index = utf8_encode($base_page . $params['index']->value);
$tree = $params['tree'];
$entry_page = $page['entry_page'];
ob_start();
?>
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head>
<title><?php echo $page['title']; ?></title>
<meta name="description" content="<?php echo $page['tagline'];?>" />
<meta name="author" content="<?php echo $page['author']; ?>">
<meta charset="UTF-8">
<link rel="icon" href="<?php echo $page['theme']['favicon']; ?>" type="image/x-icon">
<!-- Mobile -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Font -->
<?php foreach ($page['theme']['fonts'] as $font) echo "<link href='$font' rel='stylesheet' type='text/css'>"; ?>
<!-- CSS -->
<?php foreach ($page['theme']['css'] as $css) echo "<link href='$css' rel='stylesheet' type='text/css'>"; ?>
</head>
<body>
<?php if ($homepage) { ?>
<!-- Homepage -->
<div class="navbar navbar-fixed-top hidden-print">
<div class="container">
<a class="brand navbar-brand pull-left" href="<?php echo $index; ?>"><?php echo $project_title; ?></a>
<p class="navbar-text pull-right">Generated by <a href="http://daux.io">Daux.io</a></p>
</div>
</div>
<?php if ($params['repo']) { ?>
<a href="https://github.com/<?php echo $params['repo']; ?>" target="_blank" id="github-ribbon" class="hidden-print"><img src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<?php } ?>
<div class="homepage-hero well container-fluid">
<div class="container">
<div class="row">
<div class="text-center col-sm-12">
<?php if ($page['tagline']) echo '<h2>' . $page['tagline'] . '</h2>'; ?>
</div>
</div>
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<?php if ($params['image']) echo '<img class="homepage-image img-responsive" src="' . $params['image'] . '" alt="' . $project_title . '">'; ?>
</div>
</div>
</div>
</div>
<div class="hero-buttons container-fluid">
<div class="container">
<div class="row">
<div class="text-center col-sm-12">
<?php
if ($params['repo']) echo '<a href="https://github.com/' . $params['repo'] . '" class="btn btn-secondary btn-hero">View On GitHub</a>';
foreach ($entry_page as $key => $node) echo '<a href="' . $node . '" class="btn btn-primary btn-hero">' . $key . '</a>';
?>
</div>
</div>
</div>
</div>
<div class="homepage-content container-fluid">
<div class="container">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<?php echo $page['content'];?>
</div>
</div>
</div>
</div>
<div class="homepage-footer well container-fluid">
<div class="container">
<div class="row">
<div class="col-sm-5 col-sm-offset-1">
<?php if (!empty($params['links'])) { ?>
<ul class="footer-nav">
<?php foreach ($params['links'] as $name => $url) echo '<li><a href="' . $url . '" target="_blank">' . $name . '</a></li>'; ?>
</ul>
<?php } ?>
</div>
<div class="col-sm-5">
<div class="pull-right">
<?php
if (!empty($params['twitter'])) {
foreach($params['twitter'] as $handle) {
?>
<div class="twitter">
<iframe allowtransparency="true" frameborder="0" scrolling="no" style="width:162px; height:20px;" src="https://platform.twitter.com/widgets/follow_button.html?screen_name=<?php echo $handle;?>&amp;show_count=false"></iframe>
</div>
<?php
}
}
?>
</div>
</div>
</div>
</div>
</div>
<?php } else { ?>
<!-- Docs -->
<?php if ($params['repo']) { ?>
<a href="https://github.com/<?php echo $params['repo']; ?>" target="_blank" id="github-ribbon" class="hidden-print"><img src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<?php } ?>
<div class="container-fluid fluid-height wrapper">
<div class="navbar navbar-fixed-top hidden-print">
<div class="container-fluid">
<a class="brand navbar-brand pull-left" href="<?php echo $index;?>"><?php echo $project_title; ?></a>
<p class="navbar-text pull-right">Generated by <a href="http://daux.io">Daux.io</a></p>
</div>
</div>
<div class="row columns content">
<div class="left-column article-tree col-sm-3 hidden-print">
<!-- For Mobile -->
<div class="responsive-collapse">
<button type="button" class="btn btn-sidebar" id="menu-spinner-button">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div id="sub-nav-collapse" class="sub-nav-collapse">
<!-- Navigation -->
<?php
if ($page['language'] !== '') echo $this->get_navigation($tree->value[$page['language']], $page['language'], $params['request'], $base_page, $params['mode']);
else echo $this->get_navigation($tree, '', $params['request'], $base_page, $params['mode']);
?>
<?php if (!empty($params['links']) || !empty($params['twitter'])) { ?>
<div class="well well-sidebar">
<!-- Links -->
<?php foreach ($params['links'] as $name => $url) echo '<a href="' . $url . '" target="_blank">' . $name . '</a><br>'; ?>
<?php if ($params['toggle_code']) echo '<a href="#" id="toggleCodeBlockBtn" onclick="toggleCodeBlocks();">Show Code Blocks Inline</a><br>'; ?>
<!-- Twitter -->
<?php foreach ($params['twitter'] as $handle) { ?>
<div class="twitter">
<hr/>
<iframe allowtransparency="true" frameborder="0" scrolling="no" style="width:162px; height:20px;" src="https://platform.twitter.com/widgets/follow_button.html?screen_name=<?php echo $handle;?>&amp;show_count=false"></iframe>
</div>
<?php } ?>
</div>
<?php } ?>
</div>
</div>
<div class="right-column <?php echo ($params['float']?'float-view':''); ?> content-area col-sm-9">
<div class="content-page">
<article>
<?php if ($params['date_modified']) { ?>
<div class="page-header sub-header clearfix">
<h1><?php
if ($page['breadcrumbs']) echo $this->get_breadcrumb_title($page, $base_page);
else echo $page['title'];
?>
<?php if ($page['file_editor']) echo '<a href="javascript:;" id="editThis" class="btn">Edit this page</a>'; ?>
</h1>
<span style="float: left; font-size: 10px; color: gray;">
<?php echo date("l, F j, Y", $page['modified_time']);?>
</span>
<span style="float: right; font-size: 10px; color: gray;">
<?php echo date("g:i A", $page['modified_time']);?>
</span>
</div>
<?php } else { ?>
<div class="page-header">
<h1><?php
if ($page['breadcrumbs']) echo $this->get_breadcrumb_title($page, $base_page);
else echo $page['title'];
?>
<?php if ($page['file_editor']) echo '<a href="javascript:;" id="editThis" class="btn">Edit this page</a>'; ?> </h1>
</div>
<?php } ?>
<?php echo $page['content']; ?>
<?php if ($page['file_editor']) { ?>
<div class="editor<?php if(!$params['date_modified']) echo ' paddingTop'; ?>">
<h3>You are editing <?php echo $page['path']; ?>&nbsp;<a href="javascript:;" class="closeEditor btn btn-warning">Close</a></h3>
<div class="navbar navbar-inverse navbar-default navbar-fixed-bottom" role="navigation">
<div class="navbar-inner">
<a href="javascript:;" class="save_editor btn btn-primary navbar-btn pull-right">Save file</a>
</div>
</div>
<textarea id="markdown_editor"><?php echo $page['markdown'];?></textarea>
<div class="clearfix"></div>
</div>
<?php } ?>
</article>
</div>
</div>
</div>
</div>
<?php } ?>
<?php echo $page['google_analytics']; ?>
<?php echo $page['piwik_analytics']; ?>
<!-- jQuery -->
<?php if ($page['theme']['require-jquery']) { ?>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
if (typeof jQuery == 'undefined')
document.write(unescape("%3Cscript src='<?php echo $base_url; ?>js/jquery-1.11.0.min.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<?php
}
if ($page['theme']['bootstrap-js']) echo '<script src="' . $base_url . 'js/bootstrap.min.js' . '"></script>';
?>
<!-- hightlight.js -->
<script src="<?php echo $base_url; ?>js/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- JS -->
<?php foreach ($page['theme']['js'] as $js) echo '<script src="' . $js . '"></script>'; ?>
<!-- Front end file editor -->
<?php if ($page['file_editor']) echo '<script src="'. $base_url. 'js/editor.js"></script>'; ?>
<script src="<?php echo $base_url; ?>js/custom.js"></script>
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</body>
</html>
<?php
$return = ob_get_contents();
@ob_end_clean();
return $return;
}
}
?>

View File

@ -1,149 +0,0 @@
<?php
namespace Todaymade\Daux;
class Template {
private function get_navigation($tree, $path, $base_page) {
$nav = '<ul class="nav nav-list">';
$nav .= $this->build_navigation($tree, $path, $base_page);
$nav .= '</ul>';
return $nav;
}
private function build_navigation($tree, $path, $base_page) {
$nav = '';
foreach ($tree->value as $url => $node) {
if ($node->type === \TodayMade\Daux\Directory_Entry::FILE_TYPE) {
if ($node->value === 'index') continue;
$link = ($path === '') ? $url : $path . '/' . $url;
$nav .= '<li><a href="' . utf8_encode($base_page . $link) . '">' . $node->title . '</a></li>';
} else {
$nav .= '<li>';
$link = ($path === '') ? $url : $path . '/' . $url;
if ($node->index_page) $nav .= '<a href="' . $base_page . $link . '" class="folder">' . $node->title . '</a>';
else $nav .= '<a href="#" class="aj-nav folder">' . $node->title . '</a>';
$nav .= '<ul class="nav nav-list">';
$new_path = ($path === '') ? $url : $path . '/' . $url;
$nav .= $this->build_navigation($node, $new_path, $base_page);
$nav .= '</ul></li>';
}
}
return $nav;
}
public function get_content($page, $params) {
$base_url = $params['base_url'];
$base_page = $params['base_page'];
$project_title = utf8_encode($params['title']);
$index = utf8_encode($base_page . $params['index']->value);
$tree = $params['tree'];
ob_start();
?>
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head>
<title><?php echo $page['title']; ?></title>
<link rel="icon" href="<?php echo $page['theme']['favicon']; ?>" type="image/x-icon">
<meta charset="UTF-8">
<!-- Mobile -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Font -->
<?php foreach ($page['theme']['fonts'] as $font) echo "<link href='$font' rel='stylesheet' type='text/css'>"; ?>
<!-- CSS -->
<?php foreach ($page['theme']['css'] as $css) echo "<link href='$css' rel='stylesheet' type='text/css'>"; ?>
</head>
<body>
<?php if ($params['repo']) { ?>
<a href="https://github.com/<?php echo $params['repo']; ?>" target="_blank" id="github-ribbon" class="hidden-print"><img src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<?php } ?>
<div class="container-fluid fluid-height wrapper">
<div class="navbar navbar-fixed-top hidden-print">
<div class="container-fluid">
<a class="brand navbar-brand pull-left" href="<?php echo $index;?>"><?php echo $project_title; ?></a>
<p class="navbar-text pull-right">Generated by <a href="http://daux.io">Daux.io</a></p>
</div>
</div>
<div class="row columns content">
<div class="left-column article-tree col-sm-3 hidden-print">
<!-- For Mobile -->
<div class="responsive-collapse">
<button type="button" class="btn btn-sidebar" id="menu-spinner-button">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div id="sub-nav-collapse" class="sub-nav-collapse">
<!-- Navigation -->
<?php echo $this->get_navigation($tree, '', $base_page); ?>
<?php if (!empty($params['links']) || !empty($params['twitter'])) { ?>
<div class="well well-sidebar">
<!-- Links -->
<?php foreach ($params['links'] as $name => $url) echo '<a href="' . $url . '" target="_blank">' . $name . '</a><br>'; ?>
<!-- Twitter -->
<?php foreach ($params['twitter'] as $handle) { ?>
<div class="twitter">
<hr/>
<iframe allowtransparency="true" frameborder="0" scrolling="no" style="width:162px; height:20px;" src="https://platform.twitter.com/widgets/follow_button.html?screen_name=<?php echo $handle;?>&amp;show_count=false"></iframe>
</div>
<?php } ?>
</div>
<?php } ?>
</div>
</div>
<div class="right-column content-area col-sm-9">
<div class="content-page">
<article>
<div class="page-header">
<h1><?php echo $page['title']; ?></h1>
</div>
<?php echo $page['content']; ?>
</article>
</div>
</div>
</div>
</div>
<?php echo $page['google_analytics']; ?>
<?php echo $page['piwik_analytics']; ?>
<!-- jQuery -->
<?php if ($page['theme']['require-jquery']) { ?>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
if (typeof jQuery == 'undefined')
document.write(unescape("%3Cscript src='<?php echo $base_url; ?>js/jquery-1.11.0.min.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<?php
}
if ($page['theme']['bootstrap-js']) echo '<script src="' . $base_url . 'js/bootstrap.min.js' . '"></script>';
?>
<!-- JS -->
<?php foreach ($page['theme']['js'] as $js) echo '<script src="' . $js . '"></script>'; ?>
<script src="<?php echo $base_url; ?>js/custom.js"></script>
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</body>
</html>
<?php
$return = ob_get_contents();
@ob_end_clean();
return $return;
}
}
?>

View File

@ -1,8 +0,0 @@
{
"favicon": "<theme_url>img/favicon-blue.png",
"css": ["<theme_url>css/daux-blue.css"],
"fonts": ["//fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700&subset=latin,cyrillic-ext,cyrillic"],
"js": [],
"require-jquery": true,
"bootstrap-js": false
}

Some files were not shown because too many files have changed in this diff Show More