Compare commits
No commits in common. "master" and "development" have entirely different histories.
master
...
developmen
@ -1,2 +0,0 @@
|
||||
vendor
|
||||
node_modules
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
85
.github/workflows/php.yml
vendored
85
.github/workflows/php.yml
vendored
@ -1,85 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
max-parallel: 15
|
||||
matrix:
|
||||
# TODO : enable tests on windows
|
||||
operating-system: [ubuntu-latest, macOS-latest]
|
||||
php-versions: ['7.2', '7.3', '7.4']
|
||||
exclude:
|
||||
- operating-system: macos-latest
|
||||
php-versions: 7.4
|
||||
|
||||
name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@master
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extension-csv: mbstring, dom, intl
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress --no-suggest
|
||||
|
||||
- name: Install tools
|
||||
run: ./scripts/install_tools.sh
|
||||
|
||||
- name: Run test suite
|
||||
run: composer run-script test
|
||||
|
||||
sonarcloud:
|
||||
name: "SonarCloud"
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@master
|
||||
with:
|
||||
php-version: 7.4
|
||||
extension-csv: mbstring, dom, intl
|
||||
coverage: pcov
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress --no-suggest
|
||||
|
||||
- name: Install tools
|
||||
run: ./scripts/install_tools.sh
|
||||
|
||||
- name: Run test suite
|
||||
run: composer run-script test -- --coverage-clover=coverage.clover --log-junit=test-report.xml
|
||||
|
||||
- name: Fix reports
|
||||
run: scripts/fix_reports.sh
|
||||
|
||||
- name: SonarCloud Scan
|
||||
uses: SonarSource/sonarcloud-github-action@v1.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
|
||||
documentation:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'dauxio/daux.io' && github.event_name != 'pull_request' && github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress --no-suggest
|
||||
- name: Generate documentation
|
||||
run: bin/daux generate --value html.plausible_domain=daux.io
|
||||
- uses: JamesIves/github-pages-deploy-action@2.0.3
|
||||
env:
|
||||
FOLDER: "static"
|
||||
BRANCH: gh-pages
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -5,12 +5,3 @@ node_modules
|
||||
static
|
||||
|
||||
/vendor
|
||||
/build
|
||||
.phpunit.result.cache
|
||||
.php_cs.cache
|
||||
coverage.clover
|
||||
test-report.xml
|
||||
|
||||
/prettier.config.js
|
||||
/.eslintrc.js
|
||||
/stylelint.config.js
|
||||
|
28
.php_cs
28
.php_cs
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->exclude('vendor')
|
||||
->exclude('templates')
|
||||
->in(__DIR__)
|
||||
;
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'@PHP70Migration' => true,
|
||||
'@PHP71Migration' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'explicit_string_variable' => false,
|
||||
'single_blank_line_before_namespace' => false,
|
||||
'no_short_echo_tag' => false,
|
||||
'blank_line_after_opening_tag' => false,
|
||||
'yoda_style' => false,
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'php_unit_internal_class' => false,
|
||||
'php_unit_test_class_requires_covers' => false,
|
||||
'phpdoc_align' => false,
|
||||
'multiline_whitespace_before_semicolons' => false,
|
||||
'ordered_class_elements' => ['use_trait', 'constant_public', 'constant_protected', 'constant_private', 'property_public', 'property_protected', 'property_private', 'construct', 'method']
|
||||
])
|
||||
->setFinder($finder)
|
||||
;
|
21
.travis.yml
Normal file
21
.travis.yml
Normal file
@ -0,0 +1,21 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- '5.6'
|
||||
- '7.0'
|
||||
- '7.1'
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
|
||||
before_script:
|
||||
- composer install --dev --prefer-source
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
|
@ -1,76 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at stephane.goetz@onigoetz.ch. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
27
Dockerfile
27
Dockerfile
@ -1,30 +1,29 @@
|
||||
FROM composer:1.10.5 AS composer
|
||||
FROM php:7
|
||||
|
||||
FROM php:7-stretch
|
||||
|
||||
RUN apt-get update && apt-get install -y libicu-dev git unzip
|
||||
RUN docker-php-ext-configure intl \
|
||||
&& docker-php-ext-install intl
|
||||
RUN apt-get update && apt-get install -y unzip && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir /daux && mkdir /build
|
||||
|
||||
WORKDIR /daux
|
||||
|
||||
COPY --from=composer /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Copy files
|
||||
COPY composer.json /daux/composer.json
|
||||
COPY composer.lock /daux/composer.lock
|
||||
|
||||
RUN composer install --prefer-dist --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader
|
||||
|
||||
COPY bin/ /daux/bin/
|
||||
COPY libs/ /daux/libs/
|
||||
COPY templates/ /daux/templates/
|
||||
COPY themes/ /daux/themes/
|
||||
COPY daux_libraries/ /daux/daux_libraries/
|
||||
COPY tipuesearch/ /daux/tipuesearch/
|
||||
COPY global.json /daux/global.json
|
||||
COPY index.php /daux/index.php
|
||||
COPY composer.json /daux/composer.json
|
||||
COPY composer.lock /daux/composer.lock
|
||||
|
||||
# Composer install
|
||||
RUN cd /daux && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||
&& php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
|
||||
&& php composer-setup.php \
|
||||
&& rm composer-setup.php \
|
||||
&& php composer.phar install --prefer-dist --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader \
|
||||
&& rm composer.phar
|
||||
|
||||
RUN ln -s /daux/bin/daux /usr/local/bin/daux
|
||||
|
||||
|
140
README.md
140
README.md
@ -1,48 +1,49 @@
|
||||
# Daux.io
|
||||
|
||||
|
||||
[![Latest Version](https://img.shields.io/github/release/dauxio/daux.io.svg?style=flat-square)](https://github.com/dauxio/daux.io/releases)
|
||||
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/dauxio/daux.io/blob/master/LICENSE.md)
|
||||
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/dauxio/daux.io/CI?style=flat-square)
|
||||
[![Build Status](https://img.shields.io/travis/dauxio/daux.io/master.svg?style=flat-square)](https://travis-ci.org/dauxio/daux.io)
|
||||
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/dauxio/daux.io.svg?style=flat-square)](https://scrutinizer-ci.com/g/dauxio/daux.io/code-structure)
|
||||
[![Quality Score](https://img.shields.io/scrutinizer/g/dauxio/daux.io.svg?style=flat-square)](https://scrutinizer-ci.com/g/dauxio/daux.io)
|
||||
[![Total Downloads](https://img.shields.io/packagist/dt/daux/daux.io.svg?style=flat-square)](https://packagist.org/packages/daux/daux.io)
|
||||
[![Total Downloads](https://img.shields.io/packagist/dt/dauxio/daux.io.svg?style=flat-square)](https://packagist.org/packages/dauxio/daux.io)
|
||||
|
||||
|
||||
**Daux.io** is a documentation generator that uses a simple folder structure and Markdown files to create custom documentation on the fly. It helps you create great looking documentation in a developer friendly way.
|
||||
|
||||
## Features
|
||||
|
||||
- 100% Mobile Responsive
|
||||
- CommonMark compliant (a Markdown specification)
|
||||
- Supports Markdown tables
|
||||
- Auto created homepage/landing page
|
||||
- Auto Syntax Highlighting
|
||||
- Auto Generated Navigation
|
||||
- 4 Built-In Themes or roll your own
|
||||
- Functional, Flat Design Style
|
||||
- Shareable/Linkable SEO Friendly URLs
|
||||
- Built On Bootstrap
|
||||
- No Build Step
|
||||
- Git/SVN Friendly
|
||||
- Supports Google Analytics and Piwik Analytics
|
||||
- Optional code float layout
|
||||
- Static Output Generation
|
||||
* 100% Mobile Responsive
|
||||
* CommonMark compliant (a Markdown specification)
|
||||
* Supports Markdown tables
|
||||
* Auto created homepage/landing page
|
||||
* Auto Syntax Highlighting
|
||||
* Auto Generated Navigation
|
||||
* 4 Built-In Themes or roll your own
|
||||
* Functional, Flat Design Style
|
||||
* Shareable/Linkable SEO Friendly URLs
|
||||
* Built On Bootstrap
|
||||
* No Build Step
|
||||
* Git/SVN Friendly
|
||||
* Supports Google Analytics and Piwik Analytics
|
||||
* Optional code float layout
|
||||
* Static Output Generation
|
||||
|
||||
## Demos
|
||||
|
||||
This is a list of sites using Daux.io:
|
||||
|
||||
- With a custom theme:
|
||||
- [Crafty](https://swissquote.github.io/crafty)
|
||||
- [Pixolution flow](https://docs.pixolution.org) \* [Soisy](https://doc.soisy.it/)
|
||||
- [Vulkan Tutorial](https://vulkan-tutorial.com) \* [3Q](https://docs.3q.video/)
|
||||
- [The Advanced RSS Environment](https://thearsse.com/manual/)
|
||||
* [Vulkan Tutorial](https://vulkan-tutorial.com)
|
||||
* [TrackJs](http://docs.trackjs.com)
|
||||
- With the default Theme
|
||||
- [Daux.io](https://daux.io/)
|
||||
_ [DoctrineWatcher](https://dsentker.github.io/WatcherDocumentation/)
|
||||
_ [DrupalGap](http://docs.drupalgap.org/8/)
|
||||
- [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
|
||||
- [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
|
||||
- [Nuntius: A PHP framework for bots](https://roysegall.github.io/nuntius-bot/)
|
||||
* [Daux.io](https://daux.io/)
|
||||
* [Gltn - An open-source word processor webapp](http://felkerdigitalmedia.com/gltn/docs/)
|
||||
* [Invade & Annex 3 - An Arma 3 Co-operative Mission](http://ia3.ahoyworld.co.uk/)
|
||||
* [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
|
||||
* [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
|
||||
* [Cumulus TV: Android TV app that turns any stream/page into a Live Channel](http://cumulustv.herokuapp.com)
|
||||
* [Nuntius: A PHP framework for bots](https://roysegall.github.io/nuntius-bot/)
|
||||
|
||||
Do you use Daux.io? Send me a pull request or open an [issue](https://github.com/dauxio/daux.io/issues) and I will add you to the list.
|
||||
|
||||
@ -53,7 +54,7 @@ Do you use Daux.io? Send me a pull request or open an [issue](https://github.com
|
||||
If you have PHP and Composer installed, you can install the dependency
|
||||
|
||||
```bash
|
||||
composer global require daux/daux.io
|
||||
composer global require dauxio/daux.io
|
||||
|
||||
# Next to your `docs` folder, run
|
||||
daux generate
|
||||
@ -68,7 +69,7 @@ If the command isn't found, ensure your `$PATH` contains `~/.composer/vendor/bin
|
||||
Or if you wish to use Docker, the start of the command will be :
|
||||
|
||||
```bash
|
||||
docker run --rm -it -w /build -v "$PWD":/build -u "$(id -u):$(id -g)" daux/daux.io daux
|
||||
docker run --rm -it -w /build -v "$PWD":/build daux/daux.io daux
|
||||
```
|
||||
|
||||
## Run on a server
|
||||
@ -97,18 +98,18 @@ You must use underscores instead of spaces. Here are some example file names and
|
||||
|
||||
**Good:**
|
||||
|
||||
- 01_Getting_Started.md = Getting Started
|
||||
- API_Calls.md = API Calls
|
||||
- 200_Something_Else-Cool.md = Something Else-Cool
|
||||
- \_5_Ways_to_Be_Happy.md = 5 Ways To Be Happy
|
||||
* 01_Getting_Started.md = Getting Started
|
||||
* API_Calls.md = API Calls
|
||||
* 200_Something_Else-Cool.md = Something Else-Cool
|
||||
* _5_Ways_to_Be_Happy.md = 5 Ways To Be Happy
|
||||
|
||||
**Bad:**
|
||||
|
||||
- File Name With Space.md = FAIL
|
||||
* File Name With Space.md = FAIL
|
||||
|
||||
## Sorting
|
||||
|
||||
To sort your files and folders in a specific way, you can prefix them with a number and underscore, e.g. `/docs/01_Hello_World.md` and `/docs/05_Features.md` This will list _Hello World_ before _Features_, overriding the default alpha-numeric sorting. The numbers will be stripped out of the navigation and urls. For the file `6 Ways to Get Rich`, you can use `/docs/_6_Ways_to_Get_Rich.md`
|
||||
To sort your files and folders in a specific way, you can prefix them with a number and underscore, e.g. `/docs/01_Hello_World.md` and `/docs/05_Features.md` This will list *Hello World* before *Features*, overriding the default alpha-numeric sorting. The numbers will be stripped out of the navigation and urls. For the file `6 Ways to Get Rich`, you can use `/docs/_6_Ways_to_Get_Rich.md`
|
||||
|
||||
## Landing page
|
||||
|
||||
@ -134,7 +135,6 @@ To customize the look and feel of your documentation, you can create a `config.j
|
||||
The `config.json` file is a simple JSON object that you can use to change some of the basic settings of the documentation.
|
||||
|
||||
### Title
|
||||
|
||||
Change the title bar in the docs
|
||||
|
||||
```json
|
||||
@ -144,13 +144,12 @@ Change the title bar in the docs
|
||||
```
|
||||
|
||||
### 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
|
||||
* daux-blue
|
||||
* daux-green
|
||||
* daux-navy
|
||||
* daux-red
|
||||
|
||||
```json
|
||||
{
|
||||
@ -159,25 +158,29 @@ We have 4 built-in Bootstrap themes. To use one of the themes, just set the `the
|
||||
```
|
||||
|
||||
### More options
|
||||
|
||||
Many other options are available:
|
||||
|
||||
- [Global options](http://daux.io/Configuration/index)
|
||||
- [HTML Options](http://daux.io/Configuration/Html_export)
|
||||
- [Confluence options](http://daux.io/Configuration/Confluence_upload)
|
||||
|
||||
## Running Remotely
|
||||
|
||||
Copy the files from the repo to a web server that can run PHP 7.2.0 or newer.
|
||||
Copy the files from the repo to a web server that can run PHP 5.4 or greater.
|
||||
|
||||
## Running Locally
|
||||
|
||||
There are several ways to run the docs locally.
|
||||
The recommended way is to run `daux serve` which will execute PHP's embedded 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.
|
||||
|
||||
By default the server will run at: <a href="http://localhost:8085" target="_blank">http://localhost:8085</a>
|
||||
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.
|
||||
|
||||
This is really only intended be used when you are writing/updating a ton of docs and want to preview the changes locally.
|
||||
**To use the optional Grunt command you will need:**
|
||||
|
||||
* Node.js
|
||||
* npm
|
||||
* Grunt.js
|
||||
* PHP 5.6 or greater
|
||||
|
||||
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>
|
||||
|
||||
## Generating a set of static files
|
||||
|
||||
@ -193,8 +196,8 @@ daux --source=docs --destination=static
|
||||
|
||||
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 mime type handler for less files, if using a custom theme.
|
||||
* A rewrite configuration, for handling clean urls.
|
||||
* A mime type handler for less files, if using a custom theme.
|
||||
|
||||
### Clean URLs
|
||||
|
||||
@ -208,22 +211,10 @@ The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`:
|
||||
<rule name="Main Rule" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add
|
||||
input="{REQUEST_FILENAME}"
|
||||
matchType="IsFile"
|
||||
negate="true"
|
||||
/>
|
||||
<add
|
||||
input="{REQUEST_FILENAME}"
|
||||
matchType="IsDirectory"
|
||||
negate="true"
|
||||
/>
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action
|
||||
type="Rewrite"
|
||||
url="index.php"
|
||||
appendQueryString="false"
|
||||
/>
|
||||
<action type="Rewrite" url="index.php" appendQueryString="false" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
@ -233,15 +224,28 @@ 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/).
|
||||
|
||||
## Docker
|
||||
|
||||
A docker configuration is also provided to run daux within a container, you can either run daux with php5 or php7.
|
||||
|
||||
```
|
||||
cd docker
|
||||
docker-compose -f docker-compose.7.yml up -d
|
||||
```
|
||||
|
||||
You can then point your browser to http://localhost:8086
|
||||
|
||||
## PHP Requirements
|
||||
|
||||
Daux.io is compatible with the [officially supported](https://www.php.net/supported-versions.php) PHP versions; 7.2.0 and up.
|
||||
Daux.io is compatible with PHP 5.6 and up.
|
||||
|
||||
The reason is because some dependencies we have (mainly Symfony and Guzzle) do not support php 5.4 anymore.
|
||||
|
||||
### Extensions
|
||||
|
||||
Daux.io needs the following PHP extensions to work : `php-mbstring` and `php-xml`.
|
||||
PHP Needs the following extension to work : `php-mbstring` and `php-xml`.
|
||||
|
||||
If you encounter an error similar to `utf8_decode() not found` this means that you're missing the `php-xml` package.
|
||||
If you encounter an error similar to `utf8_decode() not found` this means that you're missing the `php-xml` package. (We've seen it happen only on PHP 7)
|
||||
|
||||
## Support
|
||||
|
||||
|
17
bin/compile
Executable file
17
bin/compile
Executable 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);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "d3/daux.io",
|
||||
"name": "daux/daux.io",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"description": "Documentation generator that uses a simple folder structure and Markdown files to create custom documentation on the fly",
|
||||
@ -17,22 +17,17 @@
|
||||
],
|
||||
"bin": ["bin/daux"],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"guzzlehttp/guzzle": "~6.0",
|
||||
"league/commonmark": "^1.0.0",
|
||||
"league/commonmark": "^0.15",
|
||||
"league/plates": "~3.1",
|
||||
"myclabs/deep-copy": "^1.5",
|
||||
"scrivo/highlight.php": "^9.15",
|
||||
"symfony/console": "^5.0",
|
||||
"symfony/http-foundation": "^5.0",
|
||||
"symfony/mime": "^5.0",
|
||||
"symfony/polyfill-intl-icu": "^1.10",
|
||||
"symfony/process": "^5.0",
|
||||
"php": ">=5.6",
|
||||
"symfony/console": "~3.0",
|
||||
"symfony/finder": "~3.0",
|
||||
"webuni/commonmark-table-extension": "0.6.*",
|
||||
"webuni/front-matter": "^1.0.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"suggest":{
|
||||
"ext-intl": "Allows to translate the modified at date"
|
||||
"symfony/process": "^3.1",
|
||||
"symfony/http-foundation": "^3.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -43,12 +38,7 @@
|
||||
"justinwalsh/daux.io": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "^1.6"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "build/phpunit",
|
||||
"test:coverage-html": "build/phpunit --coverage-html=build/coverage",
|
||||
"lint": "build/php-cs-fixer fix --config=.php_cs --dry-run -v",
|
||||
"lint:fix": "build/php-cs-fixer fix --config=.php_cs"
|
||||
"phpunit/phpunit": "~5",
|
||||
"mikey179/vfsStream": "^1.6"
|
||||
}
|
||||
}
|
||||
|
2510
composer.lock
generated
2510
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@
|
||||
module.exports = {
|
||||
browsers: "defaults, not op_mini all",
|
||||
presets: [
|
||||
"@swissquote/crafty-preset-babel",
|
||||
"@swissquote/crafty-runner-rollup",
|
||||
"@swissquote/crafty-preset-postcss",
|
||||
"@swissquote/crafty-runner-gulp"
|
||||
],
|
||||
destination_css: ".",
|
||||
destination_js: ".",
|
||||
stylelint_pattern: [
|
||||
"src/css/**/*.scss",
|
||||
"!*.min.css",
|
||||
"!**/vendor/**/*.scss"
|
||||
],
|
||||
stylelint: {
|
||||
rules: {
|
||||
"swissquote/no-type-outside-scope": null,
|
||||
"plugin/no-unsupported-browser-features": null
|
||||
}
|
||||
},
|
||||
js: {
|
||||
search: {
|
||||
runner: "rollup",
|
||||
format: "iife",
|
||||
source: "src/js/search/index.js",
|
||||
destination: "daux_libraries/search.min.js"
|
||||
},
|
||||
theme_daux: {
|
||||
runner: "rollup",
|
||||
format: "iife",
|
||||
source: "src/js/theme_daux/index.js",
|
||||
destination: "themes/daux/js/daux.min.js"
|
||||
}
|
||||
},
|
||||
css: {
|
||||
theme_blue: {
|
||||
source: "src/css/theme_daux/theme-blue.scss",
|
||||
destination: "themes/daux/css/theme-blue.min.css",
|
||||
watch: ["src/css/**/*.scss"]
|
||||
},
|
||||
theme_green: {
|
||||
source: "src/css/theme_daux/theme-green.scss",
|
||||
destination: "themes/daux/css/theme-green.min.css",
|
||||
watch: ["src/css/**/*.scss"]
|
||||
},
|
||||
theme_navy: {
|
||||
source: "src/css/theme_daux/theme-navy.scss",
|
||||
destination: "themes/daux/css/theme-navy.min.css",
|
||||
watch: ["src/css/**/*.scss"]
|
||||
},
|
||||
theme_red: {
|
||||
source: "src/css/theme_daux/theme-red.scss",
|
||||
destination: "themes/daux/css/theme-red.min.css",
|
||||
watch: ["src/css/**/*.scss"]
|
||||
},
|
||||
daux_singlepage: {
|
||||
source: "src/css/theme_daux_singlepage/main.scss",
|
||||
destination: "themes/daux_singlepage/css/main.min.css",
|
||||
watch: ["src/css/**/*.scss"]
|
||||
}
|
||||
},
|
||||
postcss(crafty, config, bundle) {
|
||||
// Add postcss-page-break
|
||||
config.processor("postcss-page-break").before("autoprefixer");
|
||||
}
|
||||
};
|
@ -1,192 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Todaymade\Daux\Extension;
|
||||
|
||||
use League\CommonMark\Block\Element\BlockQuote;
|
||||
use League\CommonMark\Block\Element\Document;
|
||||
use League\CommonMark\Block\Element\Paragraph;
|
||||
use League\CommonMark\Environment;
|
||||
use League\CommonMark\ElementRendererInterface;
|
||||
use League\CommonMark\InlineParserContext;
|
||||
use League\CommonMark\HtmlElement;
|
||||
use League\CommonMark\Inline\Parser\AbstractInlineParser;
|
||||
use League\CommonMark\Block\Renderer\BlockRendererInterface;
|
||||
use League\CommonMark\Inline\Renderer\InlineRendererInterface;
|
||||
USE League\CommonMark\Block\Element\AbstractBlock;
|
||||
USE League\CommonMark\Inline\Element\AbstractInline;
|
||||
use League\CommonMark\Inline\Element\Code;
|
||||
use League\CommonMark\Inline\Element\Text;
|
||||
use League\CommonMark\Util\Configuration;
|
||||
use Todaymade\Daux\Daux;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
||||
class d3Parser extends AbstractInlineParser
|
||||
{
|
||||
public function getCharacters() {
|
||||
return ['v', 'V'];
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext)
|
||||
{
|
||||
$cursor = $inlineContext->getCursor();
|
||||
|
||||
if ($cursor->match('/D3/')) {
|
||||
$inlineContext->getContainer()->appendChild(new Code('d3logo'));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class d3BlockQuoteRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
|
||||
{
|
||||
if (!($block instanceof BlockQuote)) {
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
$attrs = [];
|
||||
foreach ($block->getData('attributes', []) as $key => $value) {
|
||||
$attrs[$key] = $htmlRenderer->escape($value, true);
|
||||
}
|
||||
|
||||
$filling = $htmlRenderer->renderBlocks($block->children());
|
||||
|
||||
if ($filling === '') {
|
||||
return new HtmlElement('blockquote', $attrs, $htmlRenderer->getOption('inner_separator', "\n"));
|
||||
}
|
||||
|
||||
if (stristr($filling, '[!!]')) {
|
||||
$attrs['class'] = isset($attrs['class']) ? $attrs['class'].' alert alert-danger' : 'alert alert-danger';
|
||||
$filling = "<i class='fas fa-exclamation-circle'></i> ".trim(str_replace('[!!]', '', $filling));
|
||||
}
|
||||
|
||||
if (stristr($filling, '[!]')) {
|
||||
$attrs['class'] = isset($attrs['class']) ? $attrs['class'].' alert alert-warning' : 'alert alert-warning';
|
||||
$filling = "<i class='fas fa-exclamation-triangle'></i> ".trim(str_replace('[!]', '', $filling));
|
||||
}
|
||||
|
||||
if (stristr($filling, '[i]')) {
|
||||
$attrs['class'] = isset($attrs['class']) ? $attrs['class'].' alert alert-info' : 'alert alert-info';
|
||||
$filling = "<i class='fas fa-info-circle'></i> ".trim(str_replace('[i]', '', $filling));
|
||||
}
|
||||
|
||||
return new HtmlElement(
|
||||
'blockquote',
|
||||
$attrs,
|
||||
$htmlRenderer->getOption('inner_separator', "\n") . $filling . $htmlRenderer->getOption('inner_separator', "\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class d3ParagraphRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
|
||||
{
|
||||
if (!($block instanceof Paragraph)) {
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
$pattern1 = '/\[\s*?(.{3,})\s*?\]/Uis';
|
||||
$replace1 = '<span class="navi_element">\\1</span>';
|
||||
|
||||
if ($inTightList) {
|
||||
$content = $htmlRenderer->renderInlines($block->children());
|
||||
$content = preg_replace($pattern1, $replace1, $content);
|
||||
|
||||
return $content;
|
||||
} else {
|
||||
$attrs = [];
|
||||
foreach ($block->getData('attributes', []) as $key => $value) {
|
||||
$attrs[$key] = $htmlRenderer->escape($value, true);
|
||||
}
|
||||
|
||||
$content = $htmlRenderer->renderInlines($block->children());
|
||||
$content = preg_replace($pattern1, $replace1, $content);
|
||||
|
||||
return new HtmlElement('p', $attrs, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class d3DocumentRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
|
||||
{
|
||||
if (!($block instanceof Document)) {
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
$wholeDoc = $htmlRenderer->renderBlocks($block->children());
|
||||
|
||||
$output = new NullOutput();
|
||||
$daux = new Daux(Daux::STATIC_MODE, $output);
|
||||
|
||||
foreach ($_SERVER['argv'] as $arg) {
|
||||
if (stristr($arg, '--source=')) {
|
||||
$docsdir = trim(str_replace('--source=', '', $arg));
|
||||
$daux->getParams()->setDocumentationDirectory($docsdir);
|
||||
}
|
||||
}
|
||||
|
||||
$daux->initializeConfiguration();
|
||||
|
||||
$params = $daux->getParams();
|
||||
if (isset($params['variables'])) {
|
||||
$variables = $params['variables'];
|
||||
if (isset($variables) && is_array($variables) && count($variables)) {
|
||||
foreach ($variables as $varname => $varvalue) {
|
||||
$pattern = '/{\$'.$varname.'}/mU';
|
||||
$wholeDoc = preg_replace($pattern, $varvalue, $wholeDoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $wholeDoc === '' ? '' : $wholeDoc . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
class d3TextRenderer implements InlineRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var Configuration
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param AbstractInline $inline
|
||||
* @param ElementRendererInterface $htmlRenderer
|
||||
* @return HtmlElement|mixed|string
|
||||
* @throws \Todaymade\Daux\Exception
|
||||
*/
|
||||
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
|
||||
{
|
||||
if (!($inline instanceof Text)) {
|
||||
throw new \InvalidArgumentException('Incompatible inline type: ' . get_class($inline));
|
||||
}
|
||||
|
||||
$content = $htmlRenderer->escape($inline->getContent());
|
||||
|
||||
$search = array(
|
||||
'D3', 'D³', 'D³'
|
||||
);
|
||||
$replace = "<i class='fab fa-d3 d3fa-color-blue'></i>";
|
||||
$content = str_replace($search, $replace, $content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
class d3processor extends \Todaymade\Daux\Processor
|
||||
{
|
||||
public function extendCommonMarkEnvironment(Environment $environment)
|
||||
{
|
||||
// format important and info code blocks
|
||||
$environment->addBlockRenderer('League\CommonMark\Block\Element\BlockQuote', new d3BlockQuoteRenderer());
|
||||
$environment->addBlockRenderer('League\CommonMark\Block\Element\Paragraph', new d3ParagraphRenderer());
|
||||
$environment->addBlockRenderer('League\CommonMark\Block\Element\Document', new d3DocumentRenderer());
|
||||
$environment->addInlineRenderer('League\CommonMark\Inline\Element\Text', new d3TextRenderer());
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
2
daux_libraries/search.min.js
vendored
2
daux_libraries/search.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
@ -12,7 +12,7 @@ services:
|
||||
- phpserver
|
||||
|
||||
phpserver:
|
||||
image: php:7.4-fpm
|
||||
image: php:7.0-fpm
|
||||
working_dir: /var/www/
|
||||
volumes:
|
||||
- ../:/var/www/
|
||||
|
@ -6,48 +6,42 @@
|
||||
|
||||
### For Authors
|
||||
|
||||
- [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md)
|
||||
- [Internal documentation links](01_Features/Internal_links.md)
|
||||
- [CommonMark compliant](01_Features/CommonMark_compliant.md)
|
||||
- [Auto created homepage/landing page](01_Features/Landing_page.md)
|
||||
- [Multiple Output Formats](01_Features/Multiple_Output_Formats.md)
|
||||
- [Multiple Languages Support](01_Features/Multilanguage.md)
|
||||
- [No Build Step](01_Features/Live_mode.md)
|
||||
- [Static Output Generation](01_Features/Static_Site_Generation.md)
|
||||
- [Table of Contents](01_Features/Table_of_contents.md)
|
||||
* [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md)
|
||||
* [Internal documentation links](01_Features/Internal_links.md)
|
||||
* [CommonMark compliant](01_Features/CommonMark_compliant.md)
|
||||
* [Auto created homepage/landing page](01_Features/Landing_page.md)
|
||||
* [Multiple Output Formats](01_Features/Multiple_Output_Formats.md)
|
||||
* [Multiple Languages Support](01_Features/Multilanguage.md)
|
||||
* [No Build Step](01_Features/Live_mode.md)
|
||||
* [Static Output Generation](01_Features/Static_Site_Generation.md)
|
||||
* [Table of Contents](01_Features/Table_of_contents.md)
|
||||
|
||||
### For Developers
|
||||
|
||||
- [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md)
|
||||
- [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md)
|
||||
- Full access to the internal API to create new pages programatically
|
||||
- Work with pages metadata
|
||||
* [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md)
|
||||
* [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md)
|
||||
* Full access to the internal API to create new pages programatically
|
||||
* Work with pages metadata
|
||||
|
||||
### For Marketing
|
||||
|
||||
- 100% Mobile Responsive
|
||||
- 4 Built-In Themes or roll your own
|
||||
- Functional, Flat Design Style
|
||||
- Optional code float layout
|
||||
- Shareable/Linkable SEO Friendly URLs
|
||||
- Supports Google Analytics and Piwik Analytics
|
||||
* 100% Mobile Responsive
|
||||
* 4 Built-In Themes or roll your own
|
||||
* Functional, Flat Design Style
|
||||
* Optional code float layout
|
||||
* Shareable/Linkable SEO Friendly URLs
|
||||
* Supports Google Analytics and Piwik Analytics
|
||||
|
||||
## Demos
|
||||
|
||||
This is a list of sites using Daux.io:
|
||||
|
||||
- With a custom theme:
|
||||
- [Crafty](https://swissquote.github.io/crafty)
|
||||
- [Pixolution flow](https://docs.pixolution.org) \* [Soisy](https://doc.soisy.it/)
|
||||
- [Vulkan Tutorial](https://vulkan-tutorial.com)
|
||||
- [3Q](https://docs.3q.video/)
|
||||
- With the default Theme
|
||||
- [Daux.io](https://daux.io/)
|
||||
_ [DoctrineWatcher](https://dsentker.github.io/WatcherDocumentation/)
|
||||
_ [DrupalGap](http://docs.drupalgap.org/8/)
|
||||
- [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
|
||||
- [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
|
||||
- [Nuntius: A PHP framework for bots](https://roysegall.github.io/nuntius-bot/)
|
||||
* [Daux.io](https://dauxio.github.io/)
|
||||
* [jDrupal](http://jdrupal.easystreet3.com/8/docs/)
|
||||
* [DrupalGap](http://docs.drupalgap.org/8/)
|
||||
* [Invade & Annex 3 - An Arma 3 Co-operative Mission](http://ia3.ahoyworld.co.uk/)
|
||||
* [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
|
||||
* [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
|
||||
|
||||
Do you use Daux.io? Send us a pull request or open an [issue](https://github.com/dauxio/daux.io/issues) and I will add you to the list.
|
||||
|
||||
@ -60,7 +54,7 @@ Do you use Daux.io? Send us a pull request or open an [issue](https://github.com
|
||||
If you have PHP and Composer installed, you can install the dependency
|
||||
|
||||
```bash
|
||||
composer global require daux/daux.io
|
||||
composer global require dauxio/daux.io
|
||||
|
||||
# Next to your `docs` folder, run
|
||||
daux generate
|
||||
@ -68,7 +62,7 @@ daux generate
|
||||
|
||||
You can then use the `daux` command line to generate your documentation.
|
||||
|
||||
If the command isn't found, ensure your `$PATH` contains `~/.composer/vendor/bin` or `~/.config/composer/vendor/bin`.
|
||||
If the command isn't found, ensure your `$PATH` contains `~/.composer/vendor/bin`
|
||||
|
||||
#### Docker
|
||||
|
||||
@ -80,10 +74,10 @@ docker run --rm -it -w /build -v "$PWD":/build daux/daux.io daux
|
||||
|
||||
Any parameter valid in the PHP version is valid in the Docker version
|
||||
|
||||
|
||||
### Writing pages
|
||||
|
||||
Creating new pages is very easy:
|
||||
|
||||
1. Create a markdown file (`*.md` or `*.markdown`)
|
||||
2. Start writing
|
||||
|
||||
@ -97,14 +91,14 @@ You must use underscores instead of spaces. Here are some example file names and
|
||||
|
||||
**Good:**
|
||||
|
||||
- 01_Getting_Started.md = Getting Started
|
||||
- API_Calls.md = API Calls
|
||||
- 200_Something_Else-Cool.md = Something Else-Cool
|
||||
- \_5_Ways_to_Be_Happy.md = 5 Ways To Be Happy
|
||||
* 01_Getting_Started.md = Getting Started
|
||||
* API_Calls.md = API Calls
|
||||
* 200_Something_Else-Cool.md = Something Else-Cool
|
||||
* _5_Ways_to_Be_Happy.md = 5 Ways To Be Happy
|
||||
|
||||
**Bad:**
|
||||
|
||||
- File Name With Space.md = FAIL
|
||||
* File Name With Space.md = FAIL
|
||||
|
||||
### See your pages
|
||||
|
||||
@ -144,13 +138,20 @@ Now that you got the basics, you can also [see what you can configure](05_Config
|
||||
|
||||
## PHP Requirements
|
||||
|
||||
Daux.io is compatible with the [officially supported](https://www.php.net/supported-versions.php) PHP versions; 7.2.0 and up.
|
||||
Daux.io is compatible with PHP 5.6 and up.
|
||||
|
||||
The reason is because some dependencies we have do not support php 5.5 anymore.
|
||||
|
||||
### Extensions
|
||||
|
||||
Daux.io needs the following PHP extensions to work : `php-mbstring` and `php-xml`.
|
||||
PHP Needs the following extension to work : `php-mbstring` and `php-xml`.
|
||||
|
||||
If you encounter an error similar to `utf8_decode() not found` this means that you're missing the `php-xml` package. (We've seen it happen only on PHP 7)
|
||||
|
||||
## Known Issues
|
||||
|
||||
- __Windows UTF-8 files support__ Files with UTF-8 characters cannot be handled on windows with PHP5, PHP7 should work fine.
|
||||
|
||||
If you encounter an error similar to `utf8_decode() not found` this means that you're missing the `php-xml` package.
|
||||
|
||||
## Support
|
||||
|
||||
|
@ -1,14 +1,17 @@
|
||||
|
||||
As we support CommonMark, a broad range of markdown features is available to you.
|
||||
|
||||
Many of the features shown below were known as Github Flavored Markdown.
|
||||
|
||||
## We all like making lists
|
||||
|
||||
We all like making lists
|
||||
------------------------
|
||||
|
||||
The above header should be an H2 tag. Now, for a list of fruits:
|
||||
|
||||
- Red Apples
|
||||
- Purple Grapes
|
||||
- Green Kiwifruits
|
||||
* Red Apples
|
||||
* Purple Grapes
|
||||
* Green Kiwifruits
|
||||
|
||||
Let's get crazy:
|
||||
|
||||
@ -34,7 +37,8 @@ What about some code **in** a list? That's insane, right?
|
||||
|
||||
Some people seem to like definition lists
|
||||
|
||||
## I am a robot
|
||||
I am a robot
|
||||
------------
|
||||
|
||||
Maybe you want to print `robot` to the console 1000 times. Why not?
|
||||
|
||||
@ -50,7 +54,8 @@ How about we throw some angle braces and ampersands in there?
|
||||
© 2004 Foo Corporation
|
||||
</div>
|
||||
|
||||
## Set in stone
|
||||
Set in stone
|
||||
------------
|
||||
|
||||
Preformatted blocks are useful for ASCII art:
|
||||
|
||||
@ -58,7 +63,7 @@ Preformatted blocks are useful for ASCII art:
|
||||
,-.
|
||||
, ,-. ,-.
|
||||
/ \ ( )-( )
|
||||
\ | ,.>-( )-<
|
||||
\ | ,.>-( )-<
|
||||
\|,' ( )-( )
|
||||
Y ___`-' `-'
|
||||
|/__/ `-'
|
||||
@ -68,7 +73,8 @@ Preformatted blocks are useful for ASCII art:
|
||||
___|_____________
|
||||
</pre>
|
||||
|
||||
## Playing the blame game
|
||||
Playing the blame game
|
||||
----------------------
|
||||
|
||||
If you need to blame someone, the best way to do so is by quoting them:
|
||||
|
||||
@ -86,23 +92,26 @@ Or perhaps someone a little less eloquent:
|
||||
> just put me under the spot here, and maybe I'm not as quick on my feet
|
||||
> as I should be in coming up with one.
|
||||
|
||||
## Table for two
|
||||
Table for two
|
||||
-------------
|
||||
|
||||
| ID | Name | Rank |
|
||||
| --- | :----------------: | ----------------: |
|
||||
| 1 | Tom Preston-Werner | Awesome |
|
||||
| 2 | Albert Einstein | Nearly as awesome |
|
||||
ID | Name | Rank
|
||||
---|:------:|------:
|
||||
1 | Tom Preston-Werner | Awesome
|
||||
2 | Albert Einstein | Nearly as awesome
|
||||
|
||||
## Crazy linking action
|
||||
Crazy linking action
|
||||
--------------------
|
||||
|
||||
I get 10 times more traffic from [Google][1] than from
|
||||
[Yahoo][2] or [MSN][3].
|
||||
I get 10 times more traffic from [Google] [1] than from
|
||||
[Yahoo] [2] or [MSN] [3].
|
||||
|
||||
[1]: http://google.com/ "Google"
|
||||
[2]: http://search.yahoo.com/ "Yahoo Search"
|
||||
[3]: http://search.msn.com/ "MSN Search"
|
||||
[1]: http://google.com/ "Google"
|
||||
[2]: http://search.yahoo.com/ "Yahoo Search"
|
||||
[3]: http://search.msn.com/ "MSN Search"
|
||||
|
||||
## Images
|
||||
Images
|
||||
------
|
||||
|
||||
Here's an image.
|
||||
|
||||
@ -110,4 +119,4 @@ Here's an image.
|
||||
|
||||
Note: to use images on a landing page (index.md), prefix the image URL with the name of the directory it appears in, omitting the numerical prefix used to order the sections. For example in this section, to display this image on the landing page (index.md), the URL for the image would be "Features/sampleimage.png" to display the same image.
|
||||
|
||||
_View the [source of this content](https://github.com/dauxio/daux.io/blob/master/docs/01_Features/CommonMark_compliant.md)._
|
||||
*View the [source of this content](https://github.com/dauxio/daux.io/blob/master/docs/01_Features/CommonMark_compliant.md).*
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
As you can see on the top of this page, you can add "Edit on Github" links to your pages, this feature can be enabled with a single parameter.
|
||||
|
||||
The value has to be the path to the root of your documentation folder in your repository.
|
||||
@ -6,6 +7,7 @@ In the value you see below, Daux's documentation is in the `docs` folder in the
|
||||
|
||||
Daux.io will handle the rest
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"html": {
|
||||
@ -20,6 +22,7 @@ While GitHub is the most popular, it isn't the only, collaborative VCS out there
|
||||
|
||||
As long as you can refer your files by a URL, you can create an edit link for your VCS with the following configuration:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"html": {
|
||||
|
@ -1,28 +1,18 @@
|
||||
---
|
||||
description: With Front Matter you can customize your pages even further.
|
||||
keywords: "Front Matter, Customize, Title, Description, Keywords, Author"
|
||||
author: Daux.io Team
|
||||
---
|
||||
|
||||
To customize your pages even further, you can add a Front Matter to your files.
|
||||
|
||||
Front Matter is a block you add at the top of your file and looks like this:
|
||||
|
||||
---
|
||||
title: Hallo Welt
|
||||
keywords: "Hallo, Hello, Welt, World, Erde, Earth"
|
||||
author: German Daux.io Team
|
||||
date: 12th December 1984
|
||||
---
|
||||
|
||||
## Changing the title
|
||||
|
||||
The only implemented customization right now is the override of the title.
|
||||
|
||||
If your file is named "Hello_World_de.md" and your front matter is the one displayed above, you will get a page named "Hallo Welt"
|
||||
|
||||
## Search Engine Optimization
|
||||
|
||||
For a better **SEO** experience you can change the `description`, `keywords` and `author` meta tags.
|
||||
|
||||
## For Developers
|
||||
|
||||
You can then access this information in each `Content` with `$content->getAttributes()` or with `$page['attributes']` in a template.
|
||||
You can then access this information in each `Content` with `$content->getAttributes()`
|
||||
|
@ -10,6 +10,7 @@ The easiest is to use PHP's built-in server.
|
||||
|
||||
For that i've included a short command, run `daux 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
|
||||
@ -27,7 +28,7 @@ To enable the same, set the toggle in the `config.json` file in the `/docs` fold
|
||||
|
||||
### Apache
|
||||
|
||||
Copy the files from the repo to a web server that can run PHP 7.2.0 or newer.
|
||||
Copy the files from the repo to a web server that can run PHP 5.6 or greater.
|
||||
|
||||
There is an included `.htaccess` for Apache web server.
|
||||
|
||||
@ -69,8 +70,8 @@ server {
|
||||
|
||||
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 mime type handler for less files, if using a custom theme.
|
||||
* A rewrite configuration, for handling clean urls.
|
||||
* A mime type handler for less files, if using a custom theme.
|
||||
|
||||
### Clean URLs
|
||||
|
||||
@ -84,22 +85,10 @@ The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`:
|
||||
<rule name="Main Rule" stopProcessing="true">
|
||||
<match url=".*" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add
|
||||
input="{REQUEST_FILENAME}"
|
||||
matchType="IsFile"
|
||||
negate="true"
|
||||
/>
|
||||
<add
|
||||
input="{REQUEST_FILENAME}"
|
||||
matchType="IsDirectory"
|
||||
negate="true"
|
||||
/>
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action
|
||||
type="Rewrite"
|
||||
url="index.php"
|
||||
appendQueryString="false"
|
||||
/>
|
||||
<action type="Rewrite" url="index.php" appendQueryString="false" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
@ -108,21 +97,3 @@ 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/).
|
||||
|
||||
## Docker
|
||||
|
||||
This is not meant for production use, but you can bundle your documentation in Daux's docker container
|
||||
|
||||
```
|
||||
FROM daux/daux.io
|
||||
|
||||
WORKDIR /daux/
|
||||
COPY docs/ /daux/docs/
|
||||
|
||||
EXPOSE 80
|
||||
ENTRYPOINT [ "php", "-S", "0.0.0.0:80", "index.php" ]
|
||||
```
|
||||
|
||||
When you add this to a `Dockerfile` and run `docker build --name my-daux-doc .` and then `docker --rm run -p 8000:80 my-daux-doc`
|
||||
|
||||
You can access your documentation at `localhost:8000`
|
||||
|
@ -9,7 +9,6 @@ Add this to your config.json :
|
||||
```
|
||||
|
||||
You will the need separate directories for each language in `docs/` folder.
|
||||
|
||||
```
|
||||
├── docs/
|
||||
│ ├── _index.md
|
||||
|
@ -6,17 +6,13 @@ Daux.io is extendable and comes by default with three export formats:
|
||||
|
||||
## Feature Matrix
|
||||
|
||||
| Feature | HTML | Single Page HTML | Confluence |
|
||||
| -----------------------: | :--: | :--------------: | :-------------------------: |
|
||||
| Multilanguage | √ | X (Planned) | X |
|
||||
| Landing Pages | √ | X | X |
|
||||
| Index Pages | √ | √ | √ |
|
||||
| Internal Links | √ | X (Planned) | √ |
|
||||
| Code Highlight | √ | X (Planned) | √ (Using macros) |
|
||||
| Live Mode | √ | X | X |
|
||||
| Pages Ordering | √ | √ | X (API Limitation) |
|
||||
| Google / Piwik analytics | √ | √ | √ (Configured on Conflence) |
|
||||
|
||||
## Confluence Example
|
||||
|
||||
You can see this documentation uploaded to Confluence : https://dauxio.atlassian.net/wiki/spaces/DOC/overview
|
||||
Feature | HTML | Single Page HTML | Confluence
|
||||
--------------:|:----:|:----------------:|:----------:
|
||||
Multilanguage | √ | X (Planned) | X
|
||||
Landing Pages | √ | X | X
|
||||
Index Pages | √ | √ | √
|
||||
Internal Links | √ | X (Planned) | √
|
||||
Code Highlight | √ | X (Planned) | √ (Using macros)
|
||||
Live Mode | √ | X | X
|
||||
Pages Ordering | √ | √ | X (API Limitation)
|
||||
Google / Piwik analytics | √ | √ | √ (Configured on Conflence)
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
## Navigation
|
||||
|
||||
The navigation is generated automatically with all pages that end with `.md` or `.markdown`
|
||||
@ -9,23 +10,6 @@ if you wish to have one defined all you need to do is add an `index.md` file to
|
||||
For example, `/docs/02_Examples` has a landing page for that section since there exists a `/docs/02_Examples/index.md` file.
|
||||
|
||||
## Sorting
|
||||
To sort your files and folders in a specific way, you can prefix them with a number and underscore, e.g. `/docs/01_Hello_World.md` and `/docs/05_Features.md` This will list *Hello World* before *Features*, overriding the default alpha-numeric sorting. The numbers will be stripped out of the navigation and urls. For the file `6 Ways to Get Rich`, you can use `/docs/_6_Ways_to_Get_Rich.md`
|
||||
|
||||
To sort your files and folders in a specific way, you can prefix them with a number and underscore, e.g. `/docs/01_Hello_World.md` and `/docs/05_Features.md`.
|
||||
This will list _Hello World_ before _Features_, overriding the default alpha-numeric sorting.
|
||||
The numbers will be stripped out of the navigation and urls. For the file `6 Ways to Get Rich`, you can use `/docs/_6_Ways_to_Get_Rich.md`
|
||||
|
||||
You might also wish to stick certain links to the bottom of a page.
|
||||
You can do so by prefixing the file name with a '-', e.g. a new file `/docs/-Contact_Us.md` will always appear at the bottom of the current list.
|
||||
Weights can also be added to further sort the bottom entries. e.g. `/docs/-01_Coming.md` will appear before `/docs/-02_Soon.md` but both will only appear after all positive or non-weighted files.
|
||||
|
||||
It works the same for files prefixed with `+`.
|
||||
|
||||
Page order priorities are like this:
|
||||
|
||||
- `+` in front of the filename and numbers in front
|
||||
- `+` in front of the filename
|
||||
- The index page
|
||||
- Numbers in the front
|
||||
- Pages without prefix
|
||||
- `-` in front of the filename and numbers in front
|
||||
- `-` in front of the filename
|
||||
You might also wish to stick certain links to the bottom of a page. You can do so by appending a '-' to the start of the filename, e.g. a new file `/docs/-Contact_Us.md` will always appear at the bottom of the current list. Weights can also be added to further sort the bottom entries. e.g. `/docs/-01_Coming.md` will appear before `/docs/-02_Soon.md` but both will only appear after all positive or non-weighted files.
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
If you don't want to serve the live version of your site, you can also generate files, these can be one of the three supported formats :
|
||||
|
||||
- HTML output
|
||||
|
@ -23,3 +23,5 @@ You can enable this feature in your configuration
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -2,30 +2,23 @@ Highlight.js highlights syntax in code examples on blogs, forums and in fact on
|
||||
|
||||
You can even use [Github Flavored Markdown](!Features/CommonMark_compliant)
|
||||
|
||||
We also use the [scrivo/highlight.php](https://github.com/scrivo/highlight.php) package to highlight on rendering when possible.
|
||||
Highlight.js is a powerful but heavy library, since we don't know which languages you'll use we included all of them.
|
||||
|
||||
The good news is; if you use a fenced code block (` ``` `) with the language defined, the rendering is done on server side and entirely skips loading Highlight.js on the page. While still having the same end-result on your code.
|
||||
**Python**
|
||||
|
||||
[TOC]
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
@requires_authorization
|
||||
def somefunc(param1='', param2=0):
|
||||
@requires_authorization
|
||||
def somefunc(param1='', param2=0):
|
||||
r'''A docstring'''
|
||||
if param1 > param2: # interesting
|
||||
print 'Gre\'ater'
|
||||
return (param2 - param1 + 1) or None
|
||||
|
||||
class SomeClass:<br> pass
|
||||
class SomeClass:<br> pass
|
||||
|
||||
>>> message = '''interpreter
|
||||
... prompt'''
|
||||
```
|
||||
>>> message = '''interpreter
|
||||
... prompt'''
|
||||
|
||||
## Python's profiler output
|
||||
|
||||
**Python's profiler output**
|
||||
|
||||
261917242 function calls in 686.251 CPU seconds
|
||||
|
||||
@ -36,7 +29,8 @@ class SomeClass:<br> pass
|
||||
153900 1.296 rrule.py:399(_iter)
|
||||
304393/151570 0.963 rrule.py:102(_iter_cached)
|
||||
|
||||
## Ruby
|
||||
|
||||
**Ruby**
|
||||
|
||||
class A < B; def self.create(object = User) object end end
|
||||
class Zebra; def inspect; "X#{2 + self.object_id}" end end
|
||||
@ -60,7 +54,7 @@ class SomeClass:<br> pass
|
||||
Constant = 1
|
||||
render action: :new
|
||||
|
||||
## Haml
|
||||
**Haml**
|
||||
|
||||
!!! XML
|
||||
%html
|
||||
@ -77,7 +71,7 @@ class SomeClass:<br> pass
|
||||
~variable4
|
||||
The current year is #{DataTime.now.year}.
|
||||
|
||||
## Perl
|
||||
**Perl**
|
||||
|
||||
# loads object
|
||||
sub load
|
||||
@ -107,7 +101,7 @@ class SomeClass:<br> pass
|
||||
=head1 NAME
|
||||
POD till the end of file
|
||||
|
||||
## PHP
|
||||
**PHP**
|
||||
|
||||
require_once 'Zend/Uri/Http.php';
|
||||
|
||||
@ -143,7 +137,7 @@ class SomeClass:<br> pass
|
||||
datahere */
|
||||
datahere
|
||||
|
||||
## Scala
|
||||
**Scala**
|
||||
|
||||
object abstractTypes extends Application {
|
||||
abstract class SeqBuffer {
|
||||
@ -178,7 +172,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
}
|
||||
|
||||
## Go
|
||||
**Go**
|
||||
|
||||
package main
|
||||
|
||||
@ -221,7 +215,7 @@ class SomeClass:<br> pass
|
||||
return
|
||||
}
|
||||
|
||||
## XML
|
||||
**XML**
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<response value="ok" xml:lang="en">
|
||||
@ -233,7 +227,8 @@ class SomeClass:<br> pass
|
||||
<a></a> <a/>
|
||||
</response>
|
||||
|
||||
## HTML (with inline css and javascript)
|
||||
|
||||
**HTML (with inline css and javascript)**
|
||||
|
||||
<!DOCTYPE html>
|
||||
<title>Title</title>
|
||||
@ -249,7 +244,7 @@ class SomeClass:<br> pass
|
||||
<!-- here goes the rest of the page -->
|
||||
</body>
|
||||
|
||||
## Lasso
|
||||
**Lasso**
|
||||
|
||||
<?LassoScript
|
||||
/* Lasso 8 */
|
||||
@ -294,7 +289,7 @@ class SomeClass:<br> pass
|
||||
^}
|
||||
?>
|
||||
|
||||
## Markdown
|
||||
**Markdown**
|
||||
|
||||
# hello world
|
||||
|
||||
@ -317,7 +312,7 @@ class SomeClass:<br> pass
|
||||
1. one thing (yeah!)
|
||||
2. two thing `i can write code`, and `more` wipee!
|
||||
|
||||
## AsciiDoc
|
||||
**AsciiDoc**
|
||||
|
||||
Hello, World!
|
||||
============
|
||||
@ -383,7 +378,7 @@ class SomeClass:<br> pass
|
||||
|
||||
NOTE: AsciiDoc is quite cool, you should try it.
|
||||
|
||||
## Django templates
|
||||
**Django templates**
|
||||
|
||||
{% if articles|length %}
|
||||
{% for article in articles %}
|
||||
@ -402,7 +397,7 @@ class SomeClass:<br> pass
|
||||
multiline.
|
||||
{% endcomment %}
|
||||
|
||||
## Handlebars
|
||||
**Handlebars**
|
||||
|
||||
<h3>Hours</h3>
|
||||
|
||||
@ -412,7 +407,7 @@ class SomeClass:<br> pass
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
## CSS
|
||||
**CSS**
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
body:first-of-type pre::after {
|
||||
@ -440,7 +435,7 @@ class SomeClass:<br> pass
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
## SCSS
|
||||
**SCSS**
|
||||
|
||||
@import "compass/reset";
|
||||
|
||||
@ -516,7 +511,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
}
|
||||
|
||||
## JSON
|
||||
**JSON**
|
||||
|
||||
[
|
||||
{
|
||||
@ -531,7 +526,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
]
|
||||
|
||||
## JavaScript
|
||||
**JavaScript**
|
||||
|
||||
function $initHighlight(block, flags) {
|
||||
try {
|
||||
@ -550,7 +545,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
}
|
||||
|
||||
## CoffeeScript
|
||||
**CoffeeScript**
|
||||
|
||||
# Divisions
|
||||
x = 6/foo/i
|
||||
@ -609,7 +604,7 @@ class SomeClass:<br> pass
|
||||
?: [-=]> # function
|
||||
) ///
|
||||
|
||||
## ActionScript
|
||||
**ActionScript**
|
||||
|
||||
package org.example.dummy {
|
||||
import org.dummy.*;
|
||||
@ -636,7 +631,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
}
|
||||
|
||||
## VB.NET
|
||||
**VB.NET**
|
||||
|
||||
Import System
|
||||
Import System.IO
|
||||
@ -681,7 +676,8 @@ class SomeClass:<br> pass
|
||||
End Class
|
||||
End Namespace
|
||||
|
||||
## HTTP
|
||||
|
||||
**HTTP**
|
||||
|
||||
POST /task?id=1 HTTP/1.1
|
||||
Host: example.org
|
||||
@ -690,7 +686,7 @@ class SomeClass:<br> pass
|
||||
|
||||
{"status": "ok", "extended": true}
|
||||
|
||||
## Lua
|
||||
**Lua**
|
||||
|
||||
--[[
|
||||
Simple signal/slot implementation
|
||||
@ -725,7 +721,7 @@ class SomeClass:<br> pass
|
||||
[[ string
|
||||
]] ]=] ]==]
|
||||
|
||||
## AppleScript
|
||||
**AppleScript**
|
||||
|
||||
repeat 5 times
|
||||
if foo is greater than bar then
|
||||
@ -742,7 +738,8 @@ class SomeClass:<br> pass
|
||||
|
||||
do shell script "/bin/echo 'hello'"
|
||||
|
||||
## Delphi
|
||||
|
||||
**Delphi**
|
||||
|
||||
TList=Class(TObject)
|
||||
Private
|
||||
@ -775,7 +772,7 @@ class SomeClass:<br> pass
|
||||
End;{Try}
|
||||
End;{CopyFile}
|
||||
|
||||
## Java
|
||||
**Java**
|
||||
|
||||
/**
|
||||
* @author John Smith <john.smith@example.com>
|
||||
@ -811,7 +808,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
}
|
||||
|
||||
## C++
|
||||
**C++**
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -828,7 +825,7 @@ class SomeClass:<br> pass
|
||||
return -2e3 + 12l;
|
||||
}
|
||||
|
||||
## Objective C
|
||||
**Objective C**
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "Dependency.h"
|
||||
@ -874,7 +871,7 @@ class SomeClass:<br> pass
|
||||
|
||||
@end
|
||||
|
||||
## Vala
|
||||
**Vala**
|
||||
|
||||
using DBus;
|
||||
|
||||
@ -923,7 +920,7 @@ class SomeClass:<br> pass
|
||||
foo.method ();
|
||||
}
|
||||
|
||||
## C
|
||||
**C**
|
||||
|
||||
using System;
|
||||
|
||||
@ -951,7 +948,7 @@ class SomeClass:<br> pass
|
||||
return urlContents.Length;
|
||||
}
|
||||
|
||||
## F
|
||||
**F**
|
||||
|
||||
open System
|
||||
|
||||
@ -1002,7 +999,7 @@ class SomeClass:<br> pass
|
||||
type s
|
||||
let minutte = 60<s>
|
||||
|
||||
## D
|
||||
**D**
|
||||
|
||||
#!/usr/bin/rdmd
|
||||
// Computes average line length for standard input.
|
||||
@ -1049,7 +1046,7 @@ class SomeClass:<br> pass
|
||||
lines ? sumLength / lines : 0);
|
||||
}
|
||||
|
||||
## RenderMan RSL
|
||||
**RenderMan RSL**
|
||||
|
||||
#define TEST_DEFINE 3.14
|
||||
/* plastic surface shader
|
||||
@ -1067,7 +1064,7 @@ class SomeClass:<br> pass
|
||||
Ci *= Oi;
|
||||
}
|
||||
|
||||
## RenderMan RIB
|
||||
**RenderMan RIB**
|
||||
|
||||
FrameBegin 0
|
||||
Display "Scene" "framebuffer" "rgb"
|
||||
@ -1095,7 +1092,7 @@ class SomeClass:<br> pass
|
||||
WorldEnd
|
||||
FrameEnd
|
||||
|
||||
## MEL (Maya Embedded Language)
|
||||
**MEL (Maya Embedded Language)**
|
||||
|
||||
proc string[] getSelectedLights()
|
||||
|
||||
@ -1123,7 +1120,7 @@ class SomeClass:<br> pass
|
||||
return $selectedLights;
|
||||
}
|
||||
|
||||
## GLSL
|
||||
**GLSL**
|
||||
|
||||
// vertex shader
|
||||
#version 150
|
||||
@ -1163,7 +1160,7 @@ class SomeClass:<br> pass
|
||||
gl_FragColor = vec4(ex_Color, 1.0);
|
||||
}
|
||||
|
||||
## SQL
|
||||
**SQL**
|
||||
|
||||
BEGIN;
|
||||
CREATE TABLE "topic" (
|
||||
@ -1181,7 +1178,7 @@ class SomeClass:<br> pass
|
||||
-- this line lacks ; at the end to allow people to be sloppy and omit it in one-liners
|
||||
COMMIT
|
||||
|
||||
## SmallTalk
|
||||
**SmallTalk**
|
||||
|
||||
Object>>method: num
|
||||
"comment 123"
|
||||
@ -1223,7 +1220,7 @@ class SomeClass:<br> pass
|
||||
sorted add: rnd next]].
|
||||
Transcript cr; show: 'Time for SortedCollection: ' , time printString , ' msecs'
|
||||
|
||||
## Lisp
|
||||
**Lisp**
|
||||
|
||||
#!/usr/bin/env csi
|
||||
|
||||
@ -1248,7 +1245,7 @@ class SomeClass:<br> pass
|
||||
(print (+ x y))
|
||||
)
|
||||
|
||||
## Clojure
|
||||
**Clojure**
|
||||
|
||||
; You must not remove this notice, or any other, from this software.
|
||||
|
||||
@ -1285,7 +1282,7 @@ class SomeClass:<br> pass
|
||||
|
||||
(. (var defn) (setMacro))
|
||||
|
||||
## Ini file
|
||||
**Ini file**
|
||||
|
||||
;Settings relating to the location and loading of the database
|
||||
[Database]
|
||||
@ -1300,7 +1297,7 @@ class SomeClass:<br> pass
|
||||
Glob=autoexec_*.ini
|
||||
AskAboutIgnoredPlugins=0
|
||||
|
||||
## Apache
|
||||
**Apache**
|
||||
|
||||
# rewrite`s rules for wordpress pretty url
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
@ -1319,7 +1316,7 @@ class SomeClass:<br> pass
|
||||
RewriteRule .? /index.php?q=${map:${lower:%1}} [NC,L]
|
||||
</Location>
|
||||
|
||||
## nginx
|
||||
**nginx**
|
||||
|
||||
user www www;
|
||||
worker_processes 2;
|
||||
@ -1369,7 +1366,7 @@ class SomeClass:<br> pass
|
||||
}
|
||||
}
|
||||
|
||||
## Diff
|
||||
**Diff**
|
||||
|
||||
Index: languages/ini.js
|
||||
===================================================================
|
||||
@ -1402,7 +1399,7 @@ class SomeClass:<br> pass
|
||||
|
||||
It is important to spell
|
||||
|
||||
## DOS batch files
|
||||
**DOS batch files**
|
||||
|
||||
cd \
|
||||
copy a b
|
||||
@ -1429,7 +1426,7 @@ class SomeClass:<br> pass
|
||||
|
||||
xcopy %1\*.* %2
|
||||
|
||||
## Bash
|
||||
**Bash**
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
@ -1462,7 +1459,7 @@ class SomeClass:<br> pass
|
||||
echo -e "# Host $1/$2 :"
|
||||
}
|
||||
|
||||
## CMake
|
||||
**CMake**
|
||||
|
||||
project(test)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
@ -1489,7 +1486,7 @@ class SomeClass:<br> pass
|
||||
|
||||
target_link_libraries(test msimg32)
|
||||
|
||||
## Axapta
|
||||
**Axapta**
|
||||
|
||||
class ExchRateLoadBatch extends RunBaseBatch {
|
||||
ExchRateLoad rbc;
|
||||
@ -1524,7 +1521,7 @@ class SomeClass:<br> pass
|
||||
return ret;
|
||||
}
|
||||
|
||||
## Oracle Rules Language
|
||||
**Oracle Rules Language**
|
||||
|
||||
//This is a comment
|
||||
ABORT "You experienced an abort.";
|
||||
@ -1563,7 +1560,7 @@ class SomeClass:<br> pass
|
||||
HNDL_1_ADD_VAL_MSG = INTDADDVMSG(HNDL_1,"Missing (Status Code 9) values found");
|
||||
EMPTY_HNDL = INTDCREATEHANDLE('05/03/2006 00:00:00', '05/03/2006 23:59:59', 3600, "Y", "0", " ");
|
||||
|
||||
## 1С
|
||||
**1С**
|
||||
|
||||
#Если Клиент Тогда
|
||||
Перем СимвольныйКодКаталога = "ля-ля-ля"; //комментарий
|
||||
@ -1582,7 +1579,7 @@ class SomeClass:<br> pass
|
||||
d = '21.01.2008'
|
||||
КонецПроцедуры
|
||||
|
||||
## AVR Assembler
|
||||
**AVR Assembler**
|
||||
|
||||
;* Title: Block Copy Routines
|
||||
;* Version: 1.1
|
||||
@ -1604,7 +1601,7 @@ class SomeClass:<br> pass
|
||||
.def ramtemp =r1 ;temporary storage register
|
||||
.def ramsize =r16 ;size of block to be copied
|
||||
|
||||
## VHDL
|
||||
**VHDL**
|
||||
|
||||
/*
|
||||
* RS-trigger with assynch. reset
|
||||
@ -1648,7 +1645,7 @@ class SomeClass:<br> pass
|
||||
nQ <= not QT;
|
||||
end architecture behaviour;
|
||||
|
||||
## Parser 3
|
||||
**Parser 3**
|
||||
|
||||
@CLASS
|
||||
base
|
||||
@ -1685,7 +1682,7 @@ class SomeClass:<br> pass
|
||||
# Isn't comment
|
||||
$result[$.hash_item1[one] $.hash_item2[two]]
|
||||
|
||||
## TeX
|
||||
**TeX**
|
||||
|
||||
\documentclass{article}
|
||||
\usepackage[koi8-r]{inputenc}
|
||||
@ -1705,7 +1702,7 @@ class SomeClass:<br> pass
|
||||
$$
|
||||
\end{document}
|
||||
|
||||
## Haskell
|
||||
**Haskell**
|
||||
|
||||
{-# LANGUAGE TypeSynonymInstances #-}
|
||||
module Network.UDP
|
||||
@ -1748,7 +1745,7 @@ class SomeClass:<br> pass
|
||||
pingUDPPort :: Socket -> SockAddr -> IO ()
|
||||
pingUDPPort s a = sendTo s (Strict.singleton 0) a >> return ()
|
||||
|
||||
## Erlang
|
||||
**Erlang**
|
||||
|
||||
-module(ssh_cli).
|
||||
|
||||
@ -1802,7 +1799,8 @@ class SomeClass:<br> pass
|
||||
union_to_list(U1)
|
||||
))).
|
||||
|
||||
## Erlang REPL
|
||||
|
||||
**Erlang REPL**
|
||||
|
||||
1> Str = "abcd".
|
||||
"abcd"
|
||||
@ -1832,7 +1830,7 @@ class SomeClass:<br> pass
|
||||
11> 1.85e+3.
|
||||
1850
|
||||
|
||||
## Rust
|
||||
**Rust**
|
||||
|
||||
use std;
|
||||
|
||||
@ -1894,7 +1892,7 @@ class SomeClass:<br> pass
|
||||
|
||||
let a: list<int> = cons(7, @cons(13, @nil));
|
||||
|
||||
## Matlab
|
||||
**Matlab**
|
||||
|
||||
n = 20; % number of points
|
||||
points = [random('unid', 100, n, 1), random('unid', 100, n, 1)];
|
||||
@ -1935,7 +1933,7 @@ class SomeClass:<br> pass
|
||||
foo_matrix = [1, 2, 3; 4, 5, 6]''';
|
||||
foo_cell = {1, 2, 3; 4, 5, 6}''.'.';
|
||||
|
||||
## R
|
||||
**R**
|
||||
|
||||
library(ggplot2)
|
||||
|
||||
@ -2006,7 +2004,7 @@ class SomeClass:<br> pass
|
||||
%"test"%
|
||||
`"test"`
|
||||
|
||||
## Mizar
|
||||
**Mizar**
|
||||
|
||||
::: ## Lambda calculus
|
||||
|
||||
|
@ -1,3 +1,2 @@
|
||||
### This is a landing page for the Examples section
|
||||
|
||||
Adding a landing page is pretty simple, all you need to do is add an "index.md" file to the related folder.
|
@ -1,23 +1,21 @@
|
||||
**Table of contents**
|
||||
__Table of contents__
|
||||
|
||||
[TOC]
|
||||
|
||||
## 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"
|
||||
"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.
|
||||
@ -38,7 +36,6 @@ You can also provide a `root_id` instead of an `ancestor_id` in this case, you s
|
||||
You can use that when you're uploading your documentation to the root of a Confluence Space or if your page already exists.
|
||||
|
||||
## 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
|
||||
@ -48,7 +45,6 @@ Because confluence can't have two pages with the same name in a space, I recomme
|
||||
```
|
||||
|
||||
## Update threshold
|
||||
|
||||
To make the upload quicker, we try to determine if a page changed or not, first with a strict comparison and if it's not completely identical, we compute the difference.
|
||||
|
||||
```json
|
||||
@ -63,8 +59,8 @@ By default the threshold is 2%.
|
||||
|
||||
Setting the value to `0` disables the feature altogether.
|
||||
|
||||
## Delete old pages
|
||||
|
||||
## Delete old pages
|
||||
When a page is renamed, there is no way to tell it was renamed, so when uploading to Confluence, the page will be uploaded and the old page will stay here.
|
||||
|
||||
By default, it will inform you that some pages aren't needed anymore and you can delete them by hand.
|
||||
@ -75,10 +71,10 @@ By default, it will inform you that some pages aren't needed anymore and you can
|
||||
}
|
||||
```
|
||||
|
||||
By setting `delete` to `true` (or running `daux` with the `--delete` flag) you tell the generator that it can safely delete the pages.
|
||||
By setting `delete` to `true` (or running `daux.phar` with the `--delete` flag) you tell the generator that it can safely delete the pages.
|
||||
|
||||
|
||||
## Information message
|
||||
|
||||
When you create your page. there is no indication that the upload process will override the content of the pages.
|
||||
|
||||
It happens sometimes that users edit the pages to add / fix an information.
|
||||
|
@ -1,11 +1,10 @@
|
||||
**Table of contents**
|
||||
__Table of contents__
|
||||
|
||||
[TOC]
|
||||
|
||||
## Analytics
|
||||
|
||||
### Google Analytics
|
||||
|
||||
This will embed the google analytics tracking code.
|
||||
|
||||
```json
|
||||
@ -15,7 +14,6 @@ This will embed the google analytics tracking code.
|
||||
```
|
||||
|
||||
### Piwik Analytics
|
||||
|
||||
This will embed the piwik tracking code.
|
||||
|
||||
```json
|
||||
@ -32,22 +30,11 @@ You can Also give a specific Piwik ID as well.
|
||||
}
|
||||
```
|
||||
|
||||
### Plausible Analytics
|
||||
|
||||
This will embed the https://plausible.io/ tracking code.
|
||||
|
||||
```json
|
||||
{
|
||||
"html": { "plausible_domain": "daux.io" }
|
||||
}
|
||||
```
|
||||
|
||||
## Automatic Table of Contents
|
||||
|
||||
You can add a table of contents on each page automatically, read about it [here](../01_Features/Table_of_contents.md)
|
||||
|
||||
## Buttons
|
||||
|
||||
## Buttons
|
||||
You can add buttons to the landing page.
|
||||
|
||||
```json
|
||||
@ -61,23 +48,31 @@ You can add buttons to the landing page.
|
||||
```
|
||||
|
||||
## Breadcrumb titles
|
||||
|
||||
Daux.io provides the option to present page titles as breadcrumb navigation.
|
||||
You can _optionally_ specify the separator used for breadcrumbs.
|
||||
You can *optionally* specify the separator used for breadcrumbs.
|
||||
|
||||
```json
|
||||
{
|
||||
"html": {
|
||||
"breadcrumbs": true,
|
||||
"breadcrumb_separator": " > "
|
||||
"breadcrumb_separator" : " > "
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Date Modified
|
||||
## 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 }
|
||||
}
|
||||
```
|
||||
|
||||
## 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`.
|
||||
To disable this, change the option in your config.json to false.
|
||||
|
||||
```json
|
||||
{
|
||||
@ -86,7 +81,6 @@ To disable this, change the option in your config.json to `false`.
|
||||
```
|
||||
|
||||
## GitHub Repo
|
||||
|
||||
Add a 'Fork me on GitHub' ribbon.
|
||||
|
||||
```json
|
||||
@ -96,7 +90,6 @@ Add a 'Fork me on GitHub' ribbon.
|
||||
```
|
||||
|
||||
## Inherit Index
|
||||
|
||||
This feature will instructs the navigation generator to seek the first available file to use when there is no index in a folder.
|
||||
|
||||
```json
|
||||
@ -106,7 +99,6 @@ This feature will instructs the navigation generator to seek the first available
|
||||
```
|
||||
|
||||
## Jump buttons
|
||||
|
||||
You can have previous/next buttons on each page.
|
||||
They can be disabled by setting `jump_buttons` to `false`.
|
||||
|
||||
@ -117,11 +109,9 @@ They can be disabled by setting `jump_buttons` to `false`.
|
||||
```
|
||||
|
||||
## Landing page
|
||||
|
||||
The automatic landing page can be disabled through the `auto_landing` option, read about it [here](../01_Features/Landing_page.md)
|
||||
|
||||
## Links
|
||||
|
||||
Include custom links in the sidebar.
|
||||
|
||||
```json
|
||||
@ -137,17 +127,15 @@ Include custom links in the sidebar.
|
||||
```
|
||||
|
||||
## Search
|
||||
|
||||
Daux has an embedded search engine read all about it [here](../01_Features/Search.md)
|
||||
|
||||
## 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
|
||||
* daux-blue
|
||||
* daux-green
|
||||
* daux-navy
|
||||
* daux-red
|
||||
|
||||
```json
|
||||
{
|
||||
@ -164,7 +152,6 @@ To use a custom theme, just copy over the theme folder into the `themes` directo
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@ -175,7 +162,6 @@ By setting the `toggle_code` property to `true`, you can offer a toggle button o
|
||||
```
|
||||
|
||||
## Twitter
|
||||
|
||||
Include twitter follow buttons in the sidebar.
|
||||
|
||||
```json
|
||||
|
@ -1,11 +1,10 @@
|
||||
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.
|
||||
|
||||
**Table of contents**
|
||||
__Table of contents__
|
||||
|
||||
[TOC]
|
||||
|
||||
### Title
|
||||
|
||||
Change the title bar in the docs
|
||||
|
||||
```json
|
||||
@ -15,7 +14,6 @@ Change the title bar in the docs
|
||||
```
|
||||
|
||||
### Tagline
|
||||
|
||||
Change the tagline bar in the docs
|
||||
|
||||
```json
|
||||
@ -25,7 +23,6 @@ Change the tagline bar in the docs
|
||||
```
|
||||
|
||||
### Author
|
||||
|
||||
Change the documentation's author
|
||||
|
||||
```json
|
||||
@ -35,7 +32,6 @@ Change the documentation's author
|
||||
```
|
||||
|
||||
### Image
|
||||
|
||||
An image to show on the landing page. A relative path from the documentation root.
|
||||
|
||||
```json
|
||||
@ -45,7 +41,6 @@ An image to show on the landing page. A relative path from the documentation roo
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
@ -55,13 +50,12 @@ be read by the integrated web server. And you set the other formats (like conflu
|
||||
}
|
||||
```
|
||||
|
||||
- **html** with [its options](./Html_export.md)
|
||||
- **confluence** with [its options](./Confluence_upload.md)
|
||||
- __html__ with [its options](./Html_export.md)
|
||||
- __confluence__ with [its options](./Confluence_upload.md)
|
||||
|
||||
Available formats are **HTML** and **Confluence**
|
||||
Available formats are __HTML__ and __Confluence__
|
||||
|
||||
### 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.
|
||||
|
||||
```json
|
||||
@ -74,7 +68,6 @@ Set custom files and entire folders to ignore within your `/docs` folder. For fi
|
||||
```
|
||||
|
||||
### 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).
|
||||
|
||||
```json
|
||||
@ -84,17 +77,15 @@ If your server does not have a default timezone set in php.ini, it may return er
|
||||
```
|
||||
|
||||
### Multi-language
|
||||
|
||||
Enables multi-language support which needs separate directories for each language in `docs/` folder.
|
||||
|
||||
```json
|
||||
{
|
||||
"languages": { "en": "English", "de": "German" }
|
||||
"languages": {"en": "English", "de": "German"}
|
||||
}
|
||||
```
|
||||
|
||||
Directory structure:
|
||||
|
||||
```
|
||||
├── docs/
|
||||
│ ├── _index.md
|
||||
@ -116,39 +107,7 @@ Directory structure:
|
||||
│ │ │ ├── 05_Code_Highlighting.md
|
||||
```
|
||||
|
||||
### Language
|
||||
|
||||
You can change the default language with the `language` option.
|
||||
|
||||
```json
|
||||
{
|
||||
"language": "fr"
|
||||
}
|
||||
```
|
||||
|
||||
This will change the language of the text within Daux.io and also the `<html lang>` attribute.
|
||||
|
||||
You can override the translations with the `strings` option.
|
||||
A string that isn't found will fall back to english.
|
||||
|
||||
```json
|
||||
{
|
||||
"strings": {
|
||||
"fr": {
|
||||
"CodeBlocks_show": "Afficher le code",
|
||||
"Search_placeholder": "Rechercher...",
|
||||
"Link_previous": "Précédent",
|
||||
"Link_next": "Suivant",
|
||||
"Edit_on": "Editer sur :name:",
|
||||
"View_on_github": "Voir sur GitHub",
|
||||
"View_documentation": "Voir la Documentation"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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).
|
||||
|
@ -1,10 +1,10 @@
|
||||
The recommended way to extend Daux is through Processors.
|
||||
|
||||
The main advantage, is that you can run it with the source or with `daux` independently. You don't need to hack in the core.
|
||||
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
|
||||
|
||||
Next to your `docs` directory, you can create a `daux` directory that can contain your Processor.
|
||||
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.
|
||||
|
||||
@ -92,3 +92,4 @@ public function addGenerators()
|
||||
return ['custom_generator' => '\Todaymade\Daux\Extension\MyNewGenerator'];
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -2,11 +2,6 @@ In its simplest form, a theme is an empty folder with a `config.json` file conta
|
||||
|
||||
After that, every setting is optional, but you can override everything if you'd like to.
|
||||
|
||||
> **Overriding styles**
|
||||
>
|
||||
> If you want to tweak a few styles, you can create a `style.css` file at the root of your documentation
|
||||
> directory and it will be included automatically. By doing this, you don't need to create a new theme.
|
||||
|
||||
## `config.json` options
|
||||
|
||||
Here is an example `config.json` file :
|
||||
@ -16,9 +11,7 @@ Here is an example `config.json` file :
|
||||
"favicon": "<theme_url>img/favicon.png",
|
||||
"css": ["<theme_url>css/theme.min.css"],
|
||||
"js": [],
|
||||
"fonts": [
|
||||
"https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700&subset=latin,cyrillic-ext,cyrillic"
|
||||
],
|
||||
"fonts": ["https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700&subset=latin,cyrillic-ext,cyrillic"],
|
||||
"variants": {
|
||||
"blue": {
|
||||
"favicon": "<theme_url>img/favicon-blue.png",
|
||||
@ -34,19 +27,19 @@ Here is an example `config.json` file :
|
||||
|
||||
There are five options :
|
||||
|
||||
- **`favicon`**: The URL to your favicon
|
||||
- **`css`**: An array of CSS Stylesheets to add to the page
|
||||
- **`js`**: An array of JavaScript files to load
|
||||
- **`fonts`**: An array of Font sources, these are added as stylesheets
|
||||
- **`variants`**: An object containing sub-themes. Each sub theme, can provide the same configurations as the main theme (`favicon`, `css`, `js`, `fonts`)
|
||||
- __`favicon`__: The URL to your favicon
|
||||
- __`css`__: An array of CSS Stylesheets to add to the page
|
||||
- __`js`__: An array of JavaScript files to load
|
||||
- __`fonts`__: An array of Font sources, these are added as stylesheets
|
||||
- __`variants`__: An object containing sub-themes. Each sub theme, can provide the same configurations as the main theme (`favicon`, `css`, `js`, `fonts`)
|
||||
|
||||
|
||||
You will also notice this `<theme_url>` in the url.
|
||||
This is automatically substituted with the final url to the theme when generating the final page.
|
||||
|
||||
There are two possible substitutions :
|
||||
|
||||
- **`<theme_url>`**: The url to the current theme
|
||||
- **`<base_url>`**: The url to the documentation root
|
||||
- __`<theme_url>`__: The url to the current theme
|
||||
- __`<base_url>`__: The url to the documentation root
|
||||
|
||||
## Theme variants
|
||||
|
||||
@ -92,7 +85,6 @@ You can create a folder named `templates` in your theme, copy-paste the original
|
||||
You can even do it one template at a time if you wish to do only small changes.
|
||||
|
||||
By default, we have the following templates :
|
||||
|
||||
- `content.php`: The content page.
|
||||
- `home.php`: The landing page.
|
||||
- `error.php`: The page to show when a page is not found or some other error happened.
|
||||
|
@ -1,4 +1,4 @@
|
||||
<p class="HeroText">
|
||||
<p class="lead">
|
||||
<strong>Daux.io</strong> is a documentation generator that uses a simple folder structure and Markdown files to create custom documentation on the fly. It helps you create great looking documentation in a developer friendly way.
|
||||
</p>
|
||||
|
||||
@ -8,42 +8,42 @@
|
||||
|
||||
---
|
||||
|
||||
<div class="Row">
|
||||
<div class="Row__third">
|
||||
<div class=row>
|
||||
<div class=col-third>
|
||||
|
||||
#### For Authors
|
||||
|
||||
- [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md)
|
||||
- [Internal documentation links](01_Features/Internal_links.md)
|
||||
- [CommonMark compliant](01_Features/CommonMark_compliant.md)
|
||||
- [Auto created homepage/landing page](01_Features/Landing_page.md)
|
||||
- [Multiple Output Formats](01_Features/Multiple_Output_Formats.md)
|
||||
- [Multiple Languages Support](01_Features/Multilanguage.md)
|
||||
- [No Build Step](01_Features/Live_mode.md)
|
||||
- [Static Output Generation](01_Features/Static_Site_Generation.md)
|
||||
- [Table of Contents](01_Features/Table_of_contents.md)
|
||||
* [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md)
|
||||
* [Internal documentation links](01_Features/Internal_links.md)
|
||||
* [CommonMark compliant](01_Features/CommonMark_compliant.md)
|
||||
* [Auto created homepage/landing page](01_Features/Landing_page.md)
|
||||
* [Multiple Output Formats](01_Features/Multiple_Output_Formats.md)
|
||||
* [Multiple Languages Support](01_Features/Multilanguage.md)
|
||||
* [No Build Step](01_Features/Live_mode.md)
|
||||
* [Static Output Generation](01_Features/Static_Site_Generation.md)
|
||||
* [Table of Contents](01_Features/Table_of_contents.md)
|
||||
|
||||
</div>
|
||||
<div class="Row__third">
|
||||
<div class=col-third>
|
||||
|
||||
#### For Developers
|
||||
|
||||
- [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md)
|
||||
- [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md)
|
||||
- Full access to the internal API to create new pages programatically
|
||||
- Work with pages metadata
|
||||
* [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md)
|
||||
* [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md)
|
||||
* Full access to the internal API to create new pages programatically
|
||||
* Work with pages metadata
|
||||
|
||||
</div>
|
||||
<div class="Row__third">
|
||||
<div class=col-third>
|
||||
|
||||
#### For Marketing
|
||||
|
||||
- 100% Mobile Responsive
|
||||
- 4 Built-In Themes or roll your own
|
||||
- Functional, Flat Design Style
|
||||
- Optional code float layout
|
||||
- Shareable/Linkable SEO Friendly URLs
|
||||
- Supports Google Analytics and Piwik Analytics
|
||||
* 100% Mobile Responsive
|
||||
* 4 Built-In Themes or roll your own
|
||||
* Functional, Flat Design Style
|
||||
* Optional code float layout
|
||||
* Shareable/Linkable SEO Friendly URLs
|
||||
* Supports Google Analytics and Piwik Analytics
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -52,16 +52,16 @@
|
||||
|
||||
### Installation and usage
|
||||
|
||||
If you have **PHP** and Composer installed
|
||||
If you have __PHP__ and Composer installed
|
||||
|
||||
```bash
|
||||
composer global require daux/daux.io
|
||||
composer global require dauxio/daux.io
|
||||
|
||||
# Next to your `docs` folder, run
|
||||
daux generate
|
||||
```
|
||||
|
||||
Or if you wish to use **Docker**
|
||||
Or if you wish to use __Docker__
|
||||
|
||||
```bash
|
||||
# Next to your `docs` folder, run
|
||||
@ -69,3 +69,18 @@ docker run --rm -it -w /build -v "$PWD":/build daux/daux.io daux generate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- Google Code -->
|
||||
<script type="text/javascript">
|
||||
var google_conversion_id = 983836026;
|
||||
var google_custom_params = window.google_tag_params;
|
||||
var google_remarketing_only = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js">
|
||||
</script>
|
||||
<noscript>
|
||||
<div style="display:inline;">
|
||||
<img height="1" width="1" style="border-style:none;" alt="" src="//googleads.g.doubleclick.net/pagead/viewthroughconversion/983836026/?value=0&guid=ON&script=0"/>
|
||||
</div>
|
||||
</noscript>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 91 KiB |
BIN
docs/app.png
BIN
docs/app.png
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 141 KiB |
@ -16,24 +16,17 @@
|
||||
"breadcrumb_separator": "Chevrons",
|
||||
"toggle_code": true,
|
||||
"date_modified": true,
|
||||
"float": true,
|
||||
"inherit_index": true,
|
||||
|
||||
"repo": "dauxio/daux.io",
|
||||
"edit_on_github": "dauxio/daux.io/blob/master/docs",
|
||||
"twitter": ["onigoetz"],
|
||||
"google_analytics": false,
|
||||
"plausible_domain": false,
|
||||
"google_analytics": "UA-3551397-7",
|
||||
"links": {
|
||||
"GitHub Repository": "https://github.com/dauxio/daux.io",
|
||||
"Help/Support/Bugs": "https://github.com/dauxio/daux.io/issues",
|
||||
"Packagist": "https://packagist.org/packages/daux/daux.io",
|
||||
"Docker Images": "https://hub.docker.com/r/daux/daux.io"
|
||||
},
|
||||
"powered_by": "Powered by Daux.io"
|
||||
},
|
||||
"confluence": {
|
||||
"base_url": "https://dauxio.atlassian.net/wiki/",
|
||||
"space_id": "DOC",
|
||||
"root_id": 196610
|
||||
"Download": "https://github.com/dauxio/daux.io/archive/master.zip",
|
||||
"GitHub Repo": "https://github.com/dauxio/daux.io",
|
||||
"Help/Support/Bugs": "https://github.com/dauxio/daux.io/issues"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
70
generate
Executable file
70
generate
Executable file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
|
||||
Daux.io
|
||||
==================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This is a tool for auto-generating documentation based on markdown files
|
||||
located in the /docs folder of the project. To see all of the available
|
||||
options and to read more about how to use the generator, visit:
|
||||
|
||||
https://dauxio.github.io/
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Justin Walsh (Todaymade): justin@todaymade.com, @justin_walsh
|
||||
Garrett Moon (Todaymade): garrett@todaymade.com, @garrett_moon
|
||||
|
||||
|
||||
Feedback & Suggestions
|
||||
----
|
||||
|
||||
To give us feedback or to suggest an idea, please create an request on the the
|
||||
GitHub issue tracker:
|
||||
|
||||
https://github.com/dauxio/daux.io/issues
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
To file bug reports please create an issue using the github issue tracker:
|
||||
|
||||
https://github.com/dauxio/daux.io/issues
|
||||
|
||||
|
||||
Copyright and License
|
||||
---------------------
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as
|
||||
is" and any express or implied warranties, including, but not limited
|
||||
to, the implied warranties of merchantability and fitness for a
|
||||
particular purpose are disclaimed. In no event shall the copyright owner
|
||||
or contributors be liable for any direct, indirect, incidental, special,
|
||||
exemplary, or consequential damages (including, but not limited to,
|
||||
procurement of substitute goods or services; loss of use, data, or
|
||||
profits; or business interruption) however caused and on any theory of
|
||||
liability, whether in contract, strict liability, or tort (including
|
||||
negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/libs/bootstrap.php');
|
||||
|
||||
$application = new \Todaymade\Daux\Console\Application();
|
||||
$application->run();
|
77
global.json
77
global.json
@ -8,8 +8,6 @@
|
||||
"image": "",
|
||||
"languages": {},
|
||||
|
||||
"cache": true,
|
||||
|
||||
"format": "html",
|
||||
"processor": "",
|
||||
|
||||
@ -18,80 +16,6 @@
|
||||
"folders": []
|
||||
},
|
||||
|
||||
"language": "en",
|
||||
|
||||
"strings": {
|
||||
"en": {
|
||||
"CodeBlocks_show": "Show Code Blocks",
|
||||
"Search_placeholder": "Search...",
|
||||
"Search_one_result": "1 result",
|
||||
"Search_results": "!count results",
|
||||
"Search_no_results": "Nothing found",
|
||||
"Search_common_words_ignored": "Common words are largely ignored",
|
||||
"Search_too_short": "Search too short",
|
||||
"Search_one_character_or_more": "Should be one character or more",
|
||||
"Search_should_be_x_or_more": "Should be !min characters or more",
|
||||
"Link_previous": "Previous",
|
||||
"Link_next": "Next",
|
||||
"Edit_on": "Edit on :name:",
|
||||
"View_on_github": "View On GitHub",
|
||||
"View_documentation": "View Documentation",
|
||||
"Table_of_contents": "Table of Contents",
|
||||
"Toggle_navigation": "Toggle navigation"
|
||||
},
|
||||
"fr": {
|
||||
"CodeBlocks_show": "Afficher le code",
|
||||
"Search_placeholder": "Rechercher...",
|
||||
"Search_one_result": "1 résultat",
|
||||
"Search_results": "!count résultats",
|
||||
"Search_no_results": "Aucun résultat trouvé",
|
||||
"Search_common_words_ignored": "Les mots communs sont ignorés",
|
||||
"Search_too_short": "Critère de recherche trop court",
|
||||
"Search_one_character_or_more": "Doit être un caractère ou plus",
|
||||
"Search_should_be_x_or_more": "Doit être !min caractère ou plus",
|
||||
"Link_previous": "Précédent",
|
||||
"Link_next": "Suivant",
|
||||
"Edit_on": "Editer sur :name:",
|
||||
"View_on_github": "Voir sur GitHub",
|
||||
"View_documentation": "Voir la Documentation",
|
||||
"Table_of_contents": "Table des matières"
|
||||
},
|
||||
"de": {
|
||||
"CodeBlocks_show": "Code-Blöcke anzeigen",
|
||||
"Search_placeholder": "Suchen...",
|
||||
"Search_one_result": "1 Ergebnis",
|
||||
"Search_results": "!count Ergebnisse",
|
||||
"Search_no_results": "Nichts gefunden",
|
||||
"Search_common_words_ignored": "Allgemeine Wörter werden weitgehend ignoriert",
|
||||
"Search_too_short": "Suche zu kurz",
|
||||
"Search_one_character_or_more": "Sollte ein Zeichen oder mehr sein",
|
||||
"Search_should_be_x_or_more": "Sollte !min Zeichen oder mehr sein",
|
||||
"Link_previous": "Zurück",
|
||||
"Link_next": "Weiter",
|
||||
"Edit_on": "Bearbeiten bei :name:",
|
||||
"View_on_github": "Bei GitHub anzeigen",
|
||||
"View_documentation": "Dokumentation anzeigen",
|
||||
"Table_of_contents": "Inhaltsverzeichnis"
|
||||
},
|
||||
"it": {
|
||||
"CodeBlocks_show": "Mostra blocchi di codice",
|
||||
"Search_one_result": "1 risultato",
|
||||
"Search_results": "!count risultati",
|
||||
"Search_no_results": "Nessun risultato trovato",
|
||||
"Search_common_words_ignored": "Le parole comuni vengono per lo più ignorate",
|
||||
"Search_too_short": "Ricerca troppo breve",
|
||||
"Search_one_character_or_more": "Dovrebbe essere composto da uno o più caratteri",
|
||||
"Search_should_be_x_or_more": "Dovrebbe essere composto da almeno !min caratteri",
|
||||
"Search_placeholder": "Cerca...",
|
||||
"Link_previous": "Pagina precedente",
|
||||
"Link_next": "Pagina successiva",
|
||||
"Edit_on": "Modifica su :name:",
|
||||
"View_on_github": "Guarda su GitHub",
|
||||
"View_documentation": "Leggi la documentazione",
|
||||
"Table_of_contents": "Contenuti"
|
||||
}
|
||||
},
|
||||
|
||||
"timezone": "America/Los_Angeles",
|
||||
|
||||
"live": {
|
||||
@ -104,6 +28,7 @@
|
||||
"breadcrumb_separator": "Chevrons",
|
||||
"toggle_code": true,
|
||||
"date_modified": false,
|
||||
"float": false,
|
||||
"auto_landing": true,
|
||||
"search": true,
|
||||
"auto_toc": false,
|
||||
|
86
gulpfile.js
Normal file
86
gulpfile.js
Normal file
@ -0,0 +1,86 @@
|
||||
var cssnano = require('cssnano'),
|
||||
gulp = require('gulp'),
|
||||
less = require('gulp-less'),
|
||||
rename = require('gulp-rename'),
|
||||
plumber = require('gulp-plumber'),
|
||||
postcss = require('gulp-postcss'),
|
||||
stylelint = require('gulp-stylelint');
|
||||
|
||||
var resources = {
|
||||
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 stylelintRules = {
|
||||
"indentation": 4,
|
||||
"selector-list-comma-newline-after": "always-multi-line",
|
||||
"selector-max-id": 0,
|
||||
|
||||
// Autoprefixer
|
||||
"at-rule-no-vendor-prefix": true,
|
||||
"media-feature-name-no-vendor-prefix": true,
|
||||
"property-no-vendor-prefix": true,
|
||||
"selector-no-vendor-prefix": true,
|
||||
"value-no-vendor-prefix": true
|
||||
};
|
||||
|
||||
var cssnanoOptions = {
|
||||
safe: true, // Disable dangerous optimisations
|
||||
filterPlugins: false, // This does very weird stuff
|
||||
autoprefixer: {
|
||||
add: true, // Add needed prefixes
|
||||
remove: true // Remove unnecessary prefixes
|
||||
}
|
||||
};
|
||||
|
||||
function createCSSTask(source, dest) {
|
||||
return function () {
|
||||
return gulp.src(source)
|
||||
.pipe(plumber())
|
||||
.pipe(less())
|
||||
.pipe(postcss([cssnano(cssnanoOptions)]))
|
||||
.pipe(rename({suffix: '.min'}))
|
||||
.pipe(gulp.dest(dest));
|
||||
}
|
||||
}
|
||||
|
||||
gulp.task('lint-css', function () {
|
||||
return gulp
|
||||
.src(['themes/**/less/**/*.less', '!themes/**/vendor/**/*.less'])
|
||||
.pipe(stylelint({
|
||||
failAfterError: true,
|
||||
config: {
|
||||
extends: 'stylelint-config-standard',
|
||||
rules: stylelintRules
|
||||
},
|
||||
syntax: 'less',
|
||||
reporters: [{
|
||||
formatter: 'string',
|
||||
console: true
|
||||
}],
|
||||
debug: true
|
||||
}));
|
||||
});
|
||||
|
||||
var style_tasks = [];
|
||||
for (var style in resources) {
|
||||
if (resources.hasOwnProperty(style)) {
|
||||
gulp.task('style_' + style, createCSSTask(resources[style].source, resources[style].dest));
|
||||
style_tasks.push('style_' + style);
|
||||
}
|
||||
}
|
||||
|
||||
style_tasks.push('lint-css');
|
||||
|
||||
gulp.task("styles", style_tasks);
|
||||
|
||||
gulp.task('watch', ['default'], function () {
|
||||
// Watch .less files
|
||||
gulp.watch('themes/**/less/**/*.less', ['styles']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['styles']);
|
@ -5,7 +5,7 @@ use ArrayObject;
|
||||
class BaseConfig extends ArrayObject
|
||||
{
|
||||
/**
|
||||
* Merge an array into the object.
|
||||
* Merge an array into the object
|
||||
*
|
||||
* @param array $newValues
|
||||
* @param bool $override
|
||||
@ -15,9 +15,8 @@ class BaseConfig extends ArrayObject
|
||||
foreach ($newValues as $key => $value) {
|
||||
// If the key doesn't exist yet,
|
||||
// we can simply set it.
|
||||
if (!array_key_exists($key, (array) $this)) {
|
||||
if (!array_key_exists($key, $this)) {
|
||||
$this[$key] = $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -37,19 +36,4 @@ class BaseConfig extends ArrayObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hasValue($key)
|
||||
{
|
||||
return array_key_exists($key, (array) $this);
|
||||
}
|
||||
|
||||
public function getValue($key)
|
||||
{
|
||||
return $this[$key];
|
||||
}
|
||||
|
||||
public function setValue($key, $value)
|
||||
{
|
||||
$this[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
103
libs/Cache.php
103
libs/Cache.php
@ -1,103 +0,0 @@
|
||||
<?php namespace Todaymade\Daux;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Cache
|
||||
{
|
||||
public static $printed = false;
|
||||
|
||||
public static function getDirectory(): string
|
||||
{
|
||||
$dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'dauxio' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if (!Cache::$printed) {
|
||||
Cache::$printed = true;
|
||||
Daux::writeln("Using cache dir '$dir'", OutputInterface::VERBOSITY_VERBOSE);
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an item in the cache for a given number of minutes.
|
||||
*/
|
||||
public static function put(string $key, string $value): void
|
||||
{
|
||||
Cache::ensureCacheDirectoryExists($path = Cache::path($key));
|
||||
file_put_contents($path, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the file cache directory if necessary.
|
||||
*/
|
||||
protected static function ensureCacheDirectoryExists(string $path): void
|
||||
{
|
||||
$parent = dirname($path);
|
||||
|
||||
if (!file_exists($parent)) {
|
||||
mkdir($parent, 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the cache.
|
||||
*/
|
||||
public static function forget(string $key): bool
|
||||
{
|
||||
$path = Cache::path($key);
|
||||
|
||||
if (file_exists($path)) {
|
||||
return unlink($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an item from the cache by key.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get(string $key): ?string
|
||||
{
|
||||
$path = Cache::path($key);
|
||||
|
||||
if (file_exists($path)) {
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full path for the given cache key.
|
||||
*/
|
||||
protected static function path(string $key): string
|
||||
{
|
||||
$parts = array_slice(str_split($hash = sha1($key), 2), 0, 2);
|
||||
|
||||
return Cache::getDirectory() . '/' . implode('/', $parts) . '/' . $hash;
|
||||
}
|
||||
|
||||
public static function clear(): void
|
||||
{
|
||||
Cache::rrmdir(Cache::getDirectory());
|
||||
}
|
||||
|
||||
protected static function rrmdir(string $dir): void
|
||||
{
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != '.' && $object != '..') {
|
||||
if (is_dir($dir . '/' . $object)) {
|
||||
Cache::rrmdir($dir . '/' . $object);
|
||||
} else {
|
||||
unlink($dir . '/' . $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
}
|
217
libs/Compiler.php
Normal file
217
libs/Compiler.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?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')
|
||||
{
|
||||
echo "Compiling a new $pharFile\n";
|
||||
|
||||
if (file_exists($pharFile)) {
|
||||
echo "-> Deleting the existing phar\n";
|
||||
unlink($pharFile);
|
||||
}
|
||||
|
||||
echo "-> Creating the new phar\n";
|
||||
$phar = new \Phar($pharFile, 0, 'daux.phar');
|
||||
$phar->setSignatureAlgorithm(\Phar::SHA1);
|
||||
|
||||
$phar->startBuffering();
|
||||
|
||||
// Daux
|
||||
echo "-> Adding all daux files\n";
|
||||
$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
|
||||
echo "-> Adding all composer dependencies\n";
|
||||
$finder = new Finder();
|
||||
$finder->files()
|
||||
->ignoreVCS(true)
|
||||
->exclude('Tests')
|
||||
->notName('*.png')
|
||||
->in(__DIR__ . '/../vendor/guzzlehttp/guzzle/src')
|
||||
->in(__DIR__ . '/../vendor/guzzlehttp/promises/src')
|
||||
->in(__DIR__ . '/../vendor/guzzlehttp/psr7/src')
|
||||
->in(__DIR__ . '/../vendor/league/commonmark/src')
|
||||
->in(__DIR__ . '/../vendor/league/plates/src')
|
||||
->in(__DIR__ . '/../vendor/myclabs/deep-copy')
|
||||
->in(__DIR__ . '/../vendor/psr/http-message/src')
|
||||
->in(__DIR__ . '/../vendor/symfony/console')
|
||||
->in(__DIR__ . '/../vendor/symfony/polyfill-mbstring')
|
||||
->in(__DIR__ . '/../vendor/symfony/process')
|
||||
->in(__DIR__ . '/../vendor/symfony/process')
|
||||
->in(__DIR__ . '/../vendor/symfony/yaml')
|
||||
->in(__DIR__ . '/../vendor/webuni/front-matter/src')
|
||||
->in(__DIR__ . '/../vendor/webuni/commonmark-table-extension/src');
|
||||
|
||||
$excluded_files = [
|
||||
'README.md',
|
||||
'composer.json',
|
||||
'LICENSE',
|
||||
'CHANGELOG.md',
|
||||
'phpunit.xml.dist',
|
||||
];
|
||||
|
||||
/** @var \SplFileInfo $file */
|
||||
$count = 0;
|
||||
foreach ($finder as $file) {
|
||||
if (in_array($file->getFilename(), $excluded_files)) {
|
||||
continue;
|
||||
}
|
||||
$count++;
|
||||
$this->addFile($phar, $file);
|
||||
}
|
||||
echo " Imported $count files\n";
|
||||
|
||||
// Composer autoload
|
||||
echo "-> Adding the composer autoloader\n";
|
||||
$this->addComposer($phar);
|
||||
|
||||
echo "-> Adding the main binary\n";
|
||||
$this->addBinary($phar);
|
||||
|
||||
echo "-> Writing the stub\n";
|
||||
$phar->setStub($this->getStub());
|
||||
|
||||
$phar->stopBuffering();
|
||||
|
||||
echo "-> Writing the licence\n";
|
||||
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../LICENSE'), false);
|
||||
|
||||
chmod($pharFile, 0775);
|
||||
|
||||
unset($phar);
|
||||
|
||||
echo "Done.\n";
|
||||
}
|
||||
|
||||
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'));
|
||||
|
||||
if (file_exists(__DIR__ . '/../vendor/composer/autoload_static.php')) {
|
||||
$content = file_get_contents(__DIR__ . '/../vendor/composer/autoload_static.php');
|
||||
$content = str_replace('__DIR__ . \'/../..\' . \'/daux\'', 'PHAR_DIR . \'/daux\'', $content);
|
||||
$phar->addFromString('vendor/composer/autoload_static.php', $content);
|
||||
}
|
||||
|
||||
$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__ . '/../bin/daux');
|
||||
$content = preg_replace('{^#!/usr/bin/env php\s*}', '', $content);
|
||||
$phar->addFromString('bin/daux', $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], [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/bin/daux';
|
||||
|
||||
__HALT_COMPILER();
|
||||
EOF;
|
||||
}
|
||||
}
|
276
libs/Config.php
276
libs/Config.php
@ -1,116 +1,88 @@
|
||||
<?php namespace Todaymade\Daux;
|
||||
|
||||
use Todaymade\Daux\Format\Confluence\Config as ConfluenceConfig;
|
||||
use Todaymade\Daux\Format\HTML\Config as HTMLConfig;
|
||||
use Todaymade\Daux\Format\HTML\Theme as Theme;
|
||||
use Todaymade\Daux\Tree\Content;
|
||||
use Todaymade\Daux\Tree\Entry;
|
||||
use Todaymade\Daux\Format\HTML\Config as HTMLConfig;
|
||||
|
||||
class Config extends BaseConfig
|
||||
{
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->getValue('title');
|
||||
}
|
||||
|
||||
public function hasAuthor(): bool
|
||||
{
|
||||
return $this->hasValue('author');
|
||||
}
|
||||
|
||||
public function getAuthor()
|
||||
{
|
||||
return $this->getValue('author');
|
||||
}
|
||||
|
||||
public function hasTagline(): bool
|
||||
{
|
||||
return $this->hasValue('tagline');
|
||||
}
|
||||
|
||||
public function getTagline()
|
||||
{
|
||||
return $this->getValue('tagline');
|
||||
return $this['title'];
|
||||
}
|
||||
|
||||
public function getCurrentPage()
|
||||
{
|
||||
return $this->getValue('current_page');
|
||||
return $this['current_page'];
|
||||
}
|
||||
|
||||
public function setCurrentPage(Content $entry)
|
||||
{
|
||||
$this->setValue('current_page', $entry);
|
||||
$this['current_page'] = $entry;
|
||||
}
|
||||
|
||||
public function getDocumentationDirectory()
|
||||
{
|
||||
return $this->getValue('docs_directory');
|
||||
return $this['docs_directory'];
|
||||
}
|
||||
|
||||
public function setDocumentationDirectory($documentationPath)
|
||||
{
|
||||
$this['docs_directory'] = $documentationPath;
|
||||
}
|
||||
|
||||
public function getThemesDirectory()
|
||||
{
|
||||
return $this->getValue('themes_directory');
|
||||
return $this['themes_directory'];
|
||||
}
|
||||
|
||||
public function setThemesDirectory($directory)
|
||||
{
|
||||
$this['themes_directory'] = $directory;
|
||||
}
|
||||
|
||||
public function setThemesPath($themePath)
|
||||
{
|
||||
$this['themes_path'] = $themePath;
|
||||
}
|
||||
|
||||
public function getThemesPath()
|
||||
{
|
||||
return $this->getValue('themes_path');
|
||||
return $this['themes_path'];
|
||||
}
|
||||
|
||||
public function setFormat($format)
|
||||
{
|
||||
$this['format'] = $format;
|
||||
}
|
||||
|
||||
public function getFormat()
|
||||
{
|
||||
return $this->getValue('format');
|
||||
return $this['format'];
|
||||
}
|
||||
|
||||
public function hasTimezone(): bool
|
||||
public function hasTimezone()
|
||||
{
|
||||
return isset($this['timezone']);
|
||||
}
|
||||
|
||||
public function getTimezone()
|
||||
{
|
||||
return $this->getValue('timezone');
|
||||
return $this['timezone'];
|
||||
}
|
||||
|
||||
public function getTree()
|
||||
public function isMultilanguage()
|
||||
{
|
||||
return $this->getValue('tree');
|
||||
}
|
||||
|
||||
public function setTree($tree)
|
||||
{
|
||||
$this->setValue('tree', $tree);
|
||||
}
|
||||
|
||||
public function isMultilanguage(): bool
|
||||
{
|
||||
return $this->hasValue('languages') && !empty($this->getValue('languages'));
|
||||
}
|
||||
|
||||
public function getLanguages(): array
|
||||
{
|
||||
return $this->getValue('languages');
|
||||
}
|
||||
|
||||
public function getLanguage(): string
|
||||
{
|
||||
return $this->getValue('language');
|
||||
}
|
||||
|
||||
public function getMode()
|
||||
{
|
||||
return $this->getValue('mode');
|
||||
return array_key_exists('languages', $this) && !empty($this['languages']);
|
||||
}
|
||||
|
||||
public function isLive()
|
||||
{
|
||||
return $this->getValue('mode') == Daux::LIVE_MODE;
|
||||
return $this['mode'] == Daux::LIVE_MODE;
|
||||
}
|
||||
|
||||
public function isStatic()
|
||||
{
|
||||
return $this->getValue('mode') == Daux::STATIC_MODE;
|
||||
return $this['mode'] == Daux::STATIC_MODE;
|
||||
}
|
||||
|
||||
public function shouldInheritIndex()
|
||||
@ -118,188 +90,34 @@ class Config extends BaseConfig
|
||||
// As the global configuration is always present, we
|
||||
// need to test for the existence of the legacy value
|
||||
// first. Then use the current value.
|
||||
if ($this->hasValue('live') && array_key_exists('inherit_index', $this['live'])) {
|
||||
if (array_key_exists('live', $this) && array_key_exists('inherit_index', $this['live'])) {
|
||||
return $this['live']['inherit_index'];
|
||||
}
|
||||
|
||||
return $this['html']['inherit_index'];
|
||||
}
|
||||
|
||||
public function getIndexKey()
|
||||
public function setConfigurationOverrideFile($override_file)
|
||||
{
|
||||
return $this->getValue('mode') == Daux::STATIC_MODE ? 'index.html' : 'index';
|
||||
$this['override_file'] = $override_file;
|
||||
}
|
||||
|
||||
public function getProcessor()
|
||||
public function getConfigurationOverrideFile()
|
||||
{
|
||||
return $this->getValue('processor');
|
||||
if (array_key_exists('override_file', $this)) {
|
||||
return $this['override_file'];
|
||||
}
|
||||
|
||||
public function getConfluenceConfiguration(): ConfluenceConfig
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getConfluenceConfiguration()
|
||||
{
|
||||
return new ConfluenceConfig($this->hasValue('confluence') ? $this->getValue('confluence') : []);
|
||||
return $this['confluence'];
|
||||
}
|
||||
|
||||
public function getHTML(): HTMLConfig
|
||||
public function getHTML()
|
||||
{
|
||||
return new HTMLConfig($this->hasValue('html') ? $this->getValue('html') : []);
|
||||
}
|
||||
|
||||
public function getTheme(): ?Theme
|
||||
{
|
||||
return $this->hasValue('theme') ? new Theme($this->getValue('theme')) : null;
|
||||
}
|
||||
|
||||
public function getValidContentExtensions()
|
||||
{
|
||||
return $this->getValue('valid_content_extensions');
|
||||
}
|
||||
|
||||
public function setValidContentExtensions(array $value)
|
||||
{
|
||||
$this->setValue('valid_content_extensions', $value);
|
||||
}
|
||||
|
||||
public function canCache()
|
||||
{
|
||||
if ($this->hasValue('cache')) {
|
||||
return $this->getValue('cache');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getCacheKey()
|
||||
{
|
||||
$cloned = [];
|
||||
foreach ($this as $key => $value) {
|
||||
$cloned[$key] = ($value instanceof Entry) ? $value->dump() : $value;
|
||||
}
|
||||
|
||||
return sha1(json_encode($cloned));
|
||||
}
|
||||
|
||||
public function hasTranslationKey($language, $key): bool
|
||||
{
|
||||
return array_key_exists($language, $this['strings']) && array_key_exists($key, $this['strings'][$language]);
|
||||
}
|
||||
|
||||
public function getTranslationKey($language, $key)
|
||||
{
|
||||
return $this['strings'][$language][$key];
|
||||
}
|
||||
|
||||
public function hasImage(): bool
|
||||
{
|
||||
return $this->hasValue('image') && !empty($this->getValue('image'));
|
||||
}
|
||||
|
||||
public function getImage()
|
||||
{
|
||||
return $this->getValue('image');
|
||||
}
|
||||
|
||||
public function setImage($value)
|
||||
{
|
||||
$this->setValue('image', $value);
|
||||
}
|
||||
|
||||
public function getLocalBase()
|
||||
{
|
||||
return $this->getValue('local_base');
|
||||
}
|
||||
|
||||
public function getTemplates()
|
||||
{
|
||||
return $this->getValue('templates');
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->getValue('base_url');
|
||||
}
|
||||
|
||||
public function getBasePage()
|
||||
{
|
||||
if ($this->isLive()) {
|
||||
$value = '//' . $this->getBaseUrl();
|
||||
if (!$this['live']['clean_urls']) {
|
||||
$value .= 'index.php/';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->getBaseUrl();
|
||||
}
|
||||
|
||||
public function hasEntryPage(): bool
|
||||
{
|
||||
return $this->hasValue('entry_page') && !empty($this->getValue('entry_page'));
|
||||
}
|
||||
|
||||
public function getEntryPage()
|
||||
{
|
||||
return $this->getValue('entry_page');
|
||||
}
|
||||
|
||||
public function setEntryPage($value)
|
||||
{
|
||||
$this->setValue('entry_page', $value);
|
||||
}
|
||||
|
||||
public function hasRequest(): bool
|
||||
{
|
||||
return $this->hasValue('request') && !empty($this->getValue('request'));
|
||||
}
|
||||
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->getValue('request');
|
||||
}
|
||||
|
||||
public function setRequest($value)
|
||||
{
|
||||
$this->setValue('request', $value);
|
||||
}
|
||||
|
||||
public function getIndex()
|
||||
{
|
||||
return $this->getValue('index');
|
||||
}
|
||||
|
||||
public function setIndex($value)
|
||||
{
|
||||
$this->setValue('index', $value);
|
||||
}
|
||||
|
||||
public function hasProcessorInstance()
|
||||
{
|
||||
return $this->hasValue('processor_instance');
|
||||
}
|
||||
|
||||
public function getProcessorInstance()
|
||||
{
|
||||
return $this->getValue('processor_instance');
|
||||
}
|
||||
|
||||
public function setProcessorInstance($value)
|
||||
{
|
||||
$this->setValue('processor_instance', $value);
|
||||
}
|
||||
|
||||
public function getIgnore()
|
||||
{
|
||||
return $this->getValue('ignore');
|
||||
}
|
||||
|
||||
public function hasHost()
|
||||
{
|
||||
return $this->hasValue('host');
|
||||
}
|
||||
|
||||
public function getHost()
|
||||
{
|
||||
return $this->getValue('host');
|
||||
return new HTMLConfig($this['html']);
|
||||
}
|
||||
}
|
||||
|
@ -1,327 +0,0 @@
|
||||
<?php namespace Todaymade\Daux;
|
||||
|
||||
class ConfigBuilder
|
||||
{
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
/** @var array */
|
||||
private $overrideValues = [];
|
||||
|
||||
private $configuration_override_file;
|
||||
|
||||
private function __construct(string $mode)
|
||||
{
|
||||
$this->config = new Config();
|
||||
$this->config['mode'] = $mode;
|
||||
$this->config['local_base'] = dirname(__DIR__);
|
||||
}
|
||||
|
||||
public static function fromFile($file): Config
|
||||
{
|
||||
return unserialize(file_get_contents($file));
|
||||
}
|
||||
|
||||
public static function withMode($mode = Daux::STATIC_MODE): ConfigBuilder
|
||||
{
|
||||
$builder = new ConfigBuilder($mode);
|
||||
$builder->loadBaseConfiguration();
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
public function with(array $values): ConfigBuilder
|
||||
{
|
||||
$this->config->merge($values);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function setValue(Config $array, $key, $value)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return $array = $value;
|
||||
}
|
||||
$keys = explode('.', $key);
|
||||
while (count($keys) > 1) {
|
||||
$key = array_shift($keys);
|
||||
if (!isset($array[$key]) || !is_array($array[$key])) {
|
||||
$array[$key] = [];
|
||||
}
|
||||
$array = &$array[$key];
|
||||
}
|
||||
$array[array_shift($keys)] = $value;
|
||||
}
|
||||
|
||||
public function withValues(array $values): ConfigBuilder
|
||||
{
|
||||
$this->overrideValues = $values;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withDocumentationDirectory($dir): ConfigBuilder
|
||||
{
|
||||
$this->config['docs_directory'] = $dir;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withValidContentExtensions(array $value): ConfigBuilder
|
||||
{
|
||||
$this->config['valid_content_extensions'] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withThemesPath($themePath): ConfigBuilder
|
||||
{
|
||||
$this->config['themes_path'] = $themePath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withThemesDirectory($directory): ConfigBuilder
|
||||
{
|
||||
$this->config['themes_directory'] = $directory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withCache(bool $value): ConfigBuilder
|
||||
{
|
||||
$this->config['cache'] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withFormat($format): ConfigBuilder
|
||||
{
|
||||
$this->config['format'] = $format;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withConfigurationOverride($file): ConfigBuilder
|
||||
{
|
||||
$this->configuration_override_file = $file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withProcessor($value): ConfigBuilder
|
||||
{
|
||||
$this->config['processor'] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withConfluenceDelete($value): ConfigBuilder
|
||||
{
|
||||
$this->config['confluence']['delete'] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function build(): Config
|
||||
{
|
||||
$this->initializeConfiguration();
|
||||
|
||||
foreach ($this->overrideValues as $value) {
|
||||
$this->setValue($this->config, $value[0], $value[1]);
|
||||
}
|
||||
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
private function resolveThemeVariant()
|
||||
{
|
||||
$theme = $this->config->getHTML()->getTheme();
|
||||
$themesPath = $this->config->getThemesPath() . DIRECTORY_SEPARATOR;
|
||||
|
||||
// If theme directory exists, we're good with that
|
||||
if (is_dir(realpath(($themesPath . $theme)))) {
|
||||
return [$theme, ''];
|
||||
}
|
||||
|
||||
$themePieces = explode('-', $theme);
|
||||
$variant = '';
|
||||
|
||||
// Do we have a variant or only a theme ?
|
||||
if (count($themePieces) > 1) {
|
||||
$variant = array_pop($themePieces);
|
||||
$theme = implode('-', $themePieces);
|
||||
}
|
||||
|
||||
if (!is_dir(realpath($themesPath . $theme))) {
|
||||
throw new \RuntimeException("Theme '{$theme}' not found");
|
||||
}
|
||||
|
||||
return [$theme, $variant];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $override_file
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function initializeConfiguration()
|
||||
{
|
||||
// Validate and set theme path
|
||||
$docs_path = $this->normalizeDocumentationPath($this->config->getDocumentationDirectory());
|
||||
$this->config['docs_directory'] = $docs_path;
|
||||
|
||||
// Read documentation overrides
|
||||
$this->loadConfiguration($docs_path . DIRECTORY_SEPARATOR . 'config.json');
|
||||
|
||||
// Read command line overrides
|
||||
$override_file = $this->getConfigurationOverride($this->configuration_override_file);
|
||||
if ($override_file !== null) {
|
||||
$this->loadConfiguration($override_file);
|
||||
}
|
||||
|
||||
// Validate and set theme path
|
||||
$this->withThemesPath($this->normalizeThemePath($this->config->getThemesDirectory()));
|
||||
|
||||
// Resolve variant once
|
||||
$theme = $this->resolveThemeVariant();
|
||||
$this->config['html']['theme'] = $theme[0];
|
||||
$this->config['html']['theme-variant'] = $theme[1];
|
||||
|
||||
// Set a valid default timezone
|
||||
if ($this->config->hasTimezone()) {
|
||||
date_default_timezone_set($this->config->getTimezone());
|
||||
} elseif (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('GMT');
|
||||
}
|
||||
|
||||
// Text search would be too slow on live server
|
||||
if ($this->config->isLive()) {
|
||||
$this->config['html']['search'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeThemePath($path)
|
||||
{
|
||||
$validPath = $this->findLocation($path, $this->config->getLocalBase(), 'dir');
|
||||
|
||||
if (!$validPath) {
|
||||
throw new Exception('The Themes directory does not exist. Check the path again : ' . $path);
|
||||
}
|
||||
|
||||
return $validPath;
|
||||
}
|
||||
|
||||
private function normalizeDocumentationPath($path)
|
||||
{
|
||||
$validPath = $this->findLocation($path, $this->config->getLocalBase(), 'dir');
|
||||
|
||||
if (!$validPath) {
|
||||
throw new Exception('The Docs directory does not exist. Check the path again : ' . $path);
|
||||
}
|
||||
|
||||
return $validPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and validate the global configuration.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadBaseConfiguration()
|
||||
{
|
||||
// Set the default configuration
|
||||
$this->config->merge([
|
||||
'docs_directory' => 'docs',
|
||||
'valid_content_extensions' => ['md', 'markdown'],
|
||||
|
||||
//Paths and tree
|
||||
'templates' => 'templates',
|
||||
|
||||
'base_url' => '',
|
||||
]);
|
||||
|
||||
// Load the global configuration
|
||||
$this->loadConfiguration($this->config->getLocalBase() . DIRECTORY_SEPARATOR . 'global.json', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $config_file
|
||||
* @param bool $optional
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private 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->config->merge($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file requested for configuration overrides.
|
||||
*
|
||||
* @param null|string $path
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return null|string the path to a file to load for configuration overrides
|
||||
*/
|
||||
private function getConfigurationOverride($path)
|
||||
{
|
||||
$validPath = $this->findLocation($path, $this->config->getLocalBase(), 'file');
|
||||
|
||||
if ($validPath === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$validPath) {
|
||||
throw new Exception('The configuration override file does not exist. Check the path again : ' . $path);
|
||||
}
|
||||
|
||||
return $validPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $path
|
||||
* @param string $basedir
|
||||
* @param string $type
|
||||
*
|
||||
* @return null|false|string
|
||||
*/
|
||||
private function findLocation($path, $basedir, $type)
|
||||
{
|
||||
// If Path is explicitly null, it's useless to go further
|
||||
if ($path === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// VFS, used only in tests
|
||||
if (substr($path, 0, 6) == 'vfs://') {
|
||||
return $path;
|
||||
}
|
||||
|
||||
// Check if it's relative to the current directory or an absolute path
|
||||
if (DauxHelper::is($path, $type)) {
|
||||
return DauxHelper::getAbsolutePath($path);
|
||||
}
|
||||
|
||||
// Check if it exists relative to Daux's root
|
||||
$newPath = $basedir . DIRECTORY_SEPARATOR . $path;
|
||||
if (DauxHelper::is($newPath, $type)) {
|
||||
return $newPath;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -10,26 +10,6 @@ class Application extends SymfonyApplication
|
||||
|
||||
$this->add(new Generate());
|
||||
$this->add(new Serve());
|
||||
$this->add(new ClearCache());
|
||||
|
||||
$app_name = 'daux/daux.io';
|
||||
|
||||
$up = '..' . DIRECTORY_SEPARATOR;
|
||||
$composer = __DIR__ . DIRECTORY_SEPARATOR . $up . $up . $up . $up . $up . 'composer.lock';
|
||||
$version = 'unknown';
|
||||
|
||||
if (file_exists($composer)) {
|
||||
$app = json_decode(file_get_contents($composer));
|
||||
$packages = $app->packages;
|
||||
|
||||
foreach ($packages as $package) {
|
||||
if ($package->name == $app_name) {
|
||||
$version = $package->version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->setVersion($version);
|
||||
$this->setName($app_name);
|
||||
$this->setDefaultCommand('generate');
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Console;
|
||||
|
||||
use Symfony\Component\Console\Command\Command as SymfonyCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\Cache;
|
||||
|
||||
class ClearCache extends SymfonyCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('clear-cache')
|
||||
->setDescription('Clears the cache');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln("Clearing cache at '" . Cache::getDirectory() . "'");
|
||||
Cache::clear();
|
||||
$output->writeln('<info>Cache cleared</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -2,66 +2,34 @@
|
||||
|
||||
use Symfony\Component\Console\Command\Command as SymfonyCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\ConfigBuilder;
|
||||
use Todaymade\Daux\Daux;
|
||||
|
||||
class DauxCommand extends SymfonyCommand
|
||||
{
|
||||
protected function configure()
|
||||
protected function prepareDaux(InputInterface $input)
|
||||
{
|
||||
$this
|
||||
->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Configuration file')
|
||||
->addOption('source', 's', InputOption::VALUE_REQUIRED, 'Where to take the documentation from')
|
||||
->addOption('processor', 'p', InputOption::VALUE_REQUIRED, 'Manipulations on the tree')
|
||||
->addOption('no-cache', null, InputOption::VALUE_NONE, 'Disable Cache')
|
||||
->addOption('themes', 't', InputOption::VALUE_REQUIRED, 'Set a different themes directory (Used by HTML format only)')
|
||||
->addOption('value', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Set different configuration values');
|
||||
}
|
||||
|
||||
protected function prepareConfig($mode, InputInterface $input, OutputInterface $output): ConfigBuilder
|
||||
{
|
||||
$builder = ConfigBuilder::withMode($mode);
|
||||
|
||||
if ($input->getOption('configuration')) {
|
||||
$builder->withConfigurationOverride($input->getOption('configuration'));
|
||||
$daux = new Daux(Daux::STATIC_MODE);
|
||||
|
||||
// Set the format if requested
|
||||
if ($input->hasOption('format') && $input->getOption('format')) {
|
||||
$daux->getParams()->setFormat($input->getOption('format'));
|
||||
}
|
||||
|
||||
// Set the source directory
|
||||
if ($input->getOption('source')) {
|
||||
$builder->withDocumentationDirectory($input->getOption('source'));
|
||||
}
|
||||
|
||||
if ($input->getOption('processor')) {
|
||||
$builder->withProcessor($input->getOption('processor'));
|
||||
}
|
||||
|
||||
if ($input->getOption('no-cache')) {
|
||||
$builder->withCache(false);
|
||||
$daux->getParams()->setDocumentationDirectory($input->getOption('source'));
|
||||
}
|
||||
|
||||
if ($input->getOption('themes')) {
|
||||
$builder->withThemesDirectory($input->getOption('themes'));
|
||||
$daux->getParams()->setThemesDirectory($input->getOption('themes'));
|
||||
}
|
||||
|
||||
if ($input->hasOption('value')) {
|
||||
$values = array_map(
|
||||
function ($value) {
|
||||
return array_map('trim', explode('=', $value));
|
||||
},
|
||||
$input->getOption('value')
|
||||
);
|
||||
$builder->withValues($values);
|
||||
$daux->initializeConfiguration($input->getOption('configuration'));
|
||||
|
||||
if ($input->hasOption('delete') && $input->getOption('delete')) {
|
||||
$daux->getParams()['confluence']['delete'] = true;
|
||||
}
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
protected function prepareProcessor(Daux $daux, $width)
|
||||
{
|
||||
$class = $daux->getProcessorClass();
|
||||
if (!empty($class)) {
|
||||
$daux->setProcessor(new $class($daux, $daux->getOutput(), $width));
|
||||
}
|
||||
return $daux;
|
||||
}
|
||||
}
|
||||
|
@ -5,44 +5,30 @@ use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
use Todaymade\Daux\ConfigBuilder;
|
||||
use Todaymade\Daux\Daux;
|
||||
|
||||
class Generate extends DauxCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$description = 'Destination folder, relative to the working directory';
|
||||
|
||||
$this
|
||||
->setName('generate')
|
||||
->setDescription('Generate documentation')
|
||||
|
||||
->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Configuration file')
|
||||
->addOption('source', 's', InputOption::VALUE_REQUIRED, 'Where to take the documentation from')
|
||||
->addOption('format', 'f', InputOption::VALUE_REQUIRED, 'Output format, html or confluence', 'html')
|
||||
->addOption('processor', 'p', InputOption::VALUE_REQUIRED, 'Manipulations on the tree')
|
||||
->addOption('themes', 't', InputOption::VALUE_REQUIRED, 'Set a different themes directory')
|
||||
|
||||
// Confluence format only
|
||||
->addOption('delete', null, InputOption::VALUE_NONE, 'Delete pages not linked to a documentation page (confluence)')
|
||||
|
||||
->addOption('destination', 'd', InputOption::VALUE_REQUIRED, $description, 'static');
|
||||
}
|
||||
|
||||
protected function prepareConfig($mode, InputInterface $input, OutputInterface $output): ConfigBuilder
|
||||
{
|
||||
$builder = parent::prepareConfig($mode, $input, $output);
|
||||
|
||||
// Set the format if requested
|
||||
if ($input->hasOption('format') && $input->getOption('format')) {
|
||||
$builder->withFormat($input->getOption('format'));
|
||||
}
|
||||
|
||||
if ($input->hasOption('delete') && $input->getOption('delete')) {
|
||||
$builder->withConfluenceDelete(true);
|
||||
}
|
||||
|
||||
return $builder;
|
||||
// HTML Format only
|
||||
->addOption('destination', 'd', InputOption::VALUE_REQUIRED, $description, 'static')
|
||||
->addOption('search', null, InputOption::VALUE_NONE, 'Generate full text search');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
@ -58,20 +44,29 @@ class Generate extends DauxCommand
|
||||
$input = new ArgvInput($argv, $this->getDefinition());
|
||||
}
|
||||
|
||||
$builder = $this->prepareConfig(Daux::STATIC_MODE, $input, $output);
|
||||
$daux = new Daux($builder->build(), $output);
|
||||
$daux = $this->prepareDaux($input);
|
||||
|
||||
$width = (new Terminal())->getWidth();
|
||||
$width = $this->getApplication()->getTerminalDimensions()[0];
|
||||
|
||||
// Instiantiate the processor if one is defined
|
||||
$this->prepareProcessor($daux, $width);
|
||||
$this->prepareProcessor($daux, $input, $output, $width);
|
||||
|
||||
// Generate the tree
|
||||
$daux->generateTree();
|
||||
|
||||
// Generate the documentation
|
||||
$daux->getGenerator()->generateAll($input, $output, $width);
|
||||
}
|
||||
|
||||
return 0;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,25 @@
|
||||
<?php namespace Todaymade\Daux\Console;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\Daux;
|
||||
|
||||
trait RunAction
|
||||
{
|
||||
protected function getLength($content)
|
||||
protected function runAction($title, OutputInterface $output, $width, \Closure $closure)
|
||||
{
|
||||
return function_exists('mb_strlen') ? mb_strlen($content) : strlen($content);
|
||||
}
|
||||
$output->write($title);
|
||||
|
||||
protected function runAction($title, $width, \Closure $closure)
|
||||
{
|
||||
$verbose = Daux::getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE;
|
||||
|
||||
Daux::write($title, $verbose);
|
||||
$length = function_exists('mb_strlen') ? mb_strlen($title) : strlen($title);
|
||||
|
||||
// 8 is the length of the label + 2 let it breathe
|
||||
$padding = $width - $this->getLength($title) - 10;
|
||||
|
||||
$padding = $width - $length - 10;
|
||||
try {
|
||||
$response = $closure(function ($content) use (&$padding, $verbose) {
|
||||
$padding -= $this->getLength($content);
|
||||
Daux::write($content, $verbose);
|
||||
});
|
||||
$response = $closure();
|
||||
} catch (\Exception $e) {
|
||||
$this->status($padding, '[ <fg=red>FAIL</fg=red> ]');
|
||||
|
||||
$output->writeln(str_pad(' ', $padding) . '[ <fg=red>FAIL</fg=red> ]');
|
||||
throw $e;
|
||||
}
|
||||
$this->status($padding, '[ <fg=green>OK</fg=green> ]');
|
||||
$output->writeln(str_pad(' ', $padding) . '[ <fg=green>OK</fg=green> ]');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function status($padding, $content)
|
||||
{
|
||||
$verbose = Daux::getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE;
|
||||
$padding = $verbose ? '' : str_pad(' ', $padding);
|
||||
Daux::writeln($padding . $content);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,29 @@
|
||||
<?php namespace Todaymade\Daux\Console;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\ProcessUtils;
|
||||
use Todaymade\Daux\Daux;
|
||||
use Todaymade\Daux\Server\Server;
|
||||
|
||||
class Serve extends DauxCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('serve')
|
||||
->setDescription('Serve documentation')
|
||||
|
||||
->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Configuration file')
|
||||
->addOption('source', 's', InputOption::VALUE_REQUIRED, 'Where to take the documentation from')
|
||||
->addOption('processor', 'p', InputOption::VALUE_REQUIRED, 'Manipulations on the tree')
|
||||
->addOption('themes', 't', InputOption::VALUE_REQUIRED, 'Set a different themes directory')
|
||||
|
||||
// Serve the current documentation
|
||||
->addOption('serve', null, InputOption::VALUE_NONE, 'Serve the current directory')
|
||||
->addOption('host', null, InputOption::VALUE_REQUIRED, 'The host to serve on', 'localhost')
|
||||
->addOption('port', null, InputOption::VALUE_REQUIRED, 'The port to serve on', 8085);
|
||||
}
|
||||
@ -26,44 +33,31 @@ class Serve extends DauxCommand
|
||||
$host = $input->getOption('host');
|
||||
$port = $input->getOption('port');
|
||||
|
||||
$builder = $this->prepareConfig(Daux::LIVE_MODE, $input, $output);
|
||||
$daux = $this->prepareDaux($input);
|
||||
|
||||
// Daux can only serve HTML
|
||||
$builder->withFormat('html');
|
||||
|
||||
$daux = new Daux($builder->build(), $output);
|
||||
|
||||
$width = (new Terminal())->getWidth();
|
||||
|
||||
// Instiantiate the processor if one is defined
|
||||
$this->prepareProcessor($daux, $width);
|
||||
|
||||
// Write the current configuration to a file to read it from the other serving side
|
||||
$file = tmpfile();
|
||||
|
||||
if ($file === false) {
|
||||
$output->writeln('<fg=red>Failed to create temporary file for configuration</fg=red>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$path = stream_get_meta_data($file)['uri'];
|
||||
fwrite($file, serialize($daux->getConfig()));
|
||||
$daux->getParams()->setFormat('html');
|
||||
|
||||
chdir(__DIR__ . '/../../');
|
||||
|
||||
putenv('DAUX_CONFIG=' . $path);
|
||||
putenv('DAUX_VERBOSITY=' . $output->getVerbosity());
|
||||
putenv('DAUX_SOURCE=' . $daux->getParams()->getDocumentationDirectory());
|
||||
putenv('DAUX_THEME=' . $daux->getParams()->getThemesPath());
|
||||
putenv('DAUX_CONFIGURATION=' . $daux->getParams()->getConfigurationOverrideFile());
|
||||
putenv('DAUX_EXTENSION=' . DAUX_EXTENSION);
|
||||
|
||||
$base = escapeshellarg(__DIR__ . '/../../');
|
||||
$binary = escapeshellarg((new PhpExecutableFinder())->find(false));
|
||||
$base = ProcessUtils::escapeArgument(__DIR__ . '/../../');
|
||||
$binary = ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
|
||||
|
||||
echo "Daux development server started on http://{$host}:{$port}/\n";
|
||||
|
||||
if (defined('HHVM_VERSION')) {
|
||||
if (version_compare(HHVM_VERSION, '3.8.0') >= 0) {
|
||||
passthru("{$binary} -m server -v Server.Type=proxygen -v Server.SourceRoot={$base}/ -v Server.IP={$host} -v Server.Port={$port} -v Server.DefaultDocument=server.php -v Server.ErrorDocument404=server.php");
|
||||
} else {
|
||||
throw new Exception("HHVM's built-in server requires HHVM >= 3.8.0.");
|
||||
}
|
||||
} else {
|
||||
passthru("{$binary} -S {$host}:{$port} {$base}/index.php");
|
||||
fclose($file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ interface ContentType
|
||||
public function __construct(Config $config);
|
||||
|
||||
/**
|
||||
* Get the file extensions supported by this Content Type.
|
||||
* Get the file extensions supported by this Content Type
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
@ -17,7 +17,6 @@ interface ContentType
|
||||
/**
|
||||
* @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);
|
||||
|
@ -18,7 +18,7 @@ class ContentTypeHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all valid content file extensions.
|
||||
* Get all valid content file extensions
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
@ -33,8 +33,9 @@ class ContentTypeHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ContentType able to handle this node.
|
||||
* Get the ContentType able to handle this node
|
||||
*
|
||||
* @param Content $node
|
||||
* @return ContentType
|
||||
*/
|
||||
public function getType(Content $node)
|
||||
|
@ -1,25 +1,22 @@
|
||||
<?php namespace Todaymade\Daux\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\DocParser;
|
||||
use League\CommonMark\Environment;
|
||||
use League\CommonMark\Extension\Autolink\AutolinkExtension;
|
||||
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
|
||||
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
|
||||
use League\CommonMark\Extension\Table\TableExtension;
|
||||
use League\CommonMark\Inline\Element as InlineElement;
|
||||
use League\CommonMark\HtmlRenderer;
|
||||
use Todaymade\Daux\Config;
|
||||
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 = [])
|
||||
{
|
||||
$environment = Environment::createCommonMarkEnvironment();
|
||||
$environment->mergeConfig($config);
|
||||
$environment->addExtension(new AutolinkExtension());
|
||||
$environment->addExtension(new SmartPunctExtension());
|
||||
$environment->addExtension(new StrikethroughExtension());
|
||||
$environment->addExtension(new TableExtension());
|
||||
|
||||
// Table of Contents
|
||||
@ -27,11 +24,12 @@ class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter
|
||||
|
||||
$this->extendEnvironment($environment, $config['daux']);
|
||||
|
||||
if ($config['daux']->hasProcessorInstance()) {
|
||||
$config['daux']->getProcessorInstance()->extendCommonMarkEnvironment($environment);
|
||||
if (array_key_exists('processor_instance', $config['daux'])) {
|
||||
$config['daux']['processor_instance']->extendCommonMarkEnvironment($environment);
|
||||
}
|
||||
|
||||
parent::__construct($config, $environment);
|
||||
$this->docParser = new DocParser($environment);
|
||||
$this->htmlRenderer = new HtmlRenderer($environment);
|
||||
}
|
||||
|
||||
protected function getLinkRenderer(Environment $environment)
|
||||
@ -41,6 +39,6 @@ class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter
|
||||
|
||||
protected function extendEnvironment(Environment $environment, Config $config)
|
||||
{
|
||||
$environment->addInlineRenderer(InlineElement\Link::class, $this->getLinkRenderer($environment));
|
||||
$environment->addInlineRenderer('Link', $this->getLinkRenderer($environment));
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
<?php namespace Todaymade\Daux\ContentTypes\Markdown;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\Cache;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\Daux;
|
||||
use Todaymade\Daux\Tree\Content;
|
||||
|
||||
class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
|
||||
@ -12,25 +9,12 @@ class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
|
||||
protected $config;
|
||||
|
||||
/** @var CommonMarkConverter */
|
||||
private $converter;
|
||||
protected $converter;
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
protected function createConverter()
|
||||
{
|
||||
return new CommonMarkConverter(['daux' => $this->config]);
|
||||
}
|
||||
|
||||
protected function getConverter()
|
||||
{
|
||||
if (!$this->converter) {
|
||||
$this->converter = $this->createConverter();
|
||||
}
|
||||
|
||||
return $this->converter;
|
||||
$this->converter = new CommonMarkConverter(['daux' => $config]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,37 +25,10 @@ class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
|
||||
return ['md', 'markdown'];
|
||||
}
|
||||
|
||||
protected function doConversion($raw)
|
||||
{
|
||||
Daux::writeln('Running conversion', OutputInterface::VERBOSITY_VERBOSE);
|
||||
|
||||
return $this->getConverter()->convertToHtml($raw);
|
||||
}
|
||||
|
||||
public function convert($raw, Content $node)
|
||||
{
|
||||
$this->config->setCurrentPage($node);
|
||||
|
||||
$can_cache = $this->config->canCache();
|
||||
|
||||
// TODO :: add daux version to cache key
|
||||
$cacheKey = $this->config->getCacheKey() . sha1($raw);
|
||||
|
||||
$payload = Cache::get($cacheKey);
|
||||
|
||||
if ($can_cache && $payload) {
|
||||
Daux::writeln('Using cached version', OutputInterface::VERBOSITY_VERBOSE);
|
||||
}
|
||||
|
||||
if (!$can_cache || !$payload) {
|
||||
Daux::writeln($can_cache ? 'Not found in the cache, generating...' : 'Cache disabled, generating...', OutputInterface::VERBOSITY_VERBOSE);
|
||||
$payload = $this->doConversion($raw);
|
||||
}
|
||||
|
||||
if ($can_cache) {
|
||||
Cache::put($cacheKey, $payload);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
return $this->converter->convertToHtml($raw);
|
||||
}
|
||||
}
|
||||
|
@ -4,58 +4,102 @@ use League\CommonMark\ElementRendererInterface;
|
||||
use League\CommonMark\HtmlElement;
|
||||
use League\CommonMark\Inline\Element\AbstractInline;
|
||||
use League\CommonMark\Inline\Element\Link;
|
||||
use League\CommonMark\Inline\Renderer\InlineRendererInterface;
|
||||
use League\CommonMark\Util\ConfigurationAwareInterface;
|
||||
use League\CommonMark\Util\ConfigurationInterface;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\DauxHelper;
|
||||
use Todaymade\Daux\Exception\LinkNotFoundException;
|
||||
use Todaymade\Daux\Tree\Entry;
|
||||
|
||||
class LinkRenderer implements InlineRendererInterface, ConfigurationAwareInterface
|
||||
class LinkRenderer extends \League\CommonMark\Inline\Renderer\LinkRenderer
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $daux;
|
||||
|
||||
/**
|
||||
* @var \League\CommonMark\Inline\Renderer\LinkRenderer
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
public function __construct($daux)
|
||||
{
|
||||
$this->daux = $daux;
|
||||
$this->parent = new \League\CommonMark\Inline\Renderer\LinkRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return Entry
|
||||
* @throws LinkNotFoundException
|
||||
*/
|
||||
protected function resolveInternalFile($url)
|
||||
{
|
||||
$triedAbsolute = false;
|
||||
|
||||
// Legacy absolute paths could start with
|
||||
// "!" In this case we will try to find
|
||||
// the file starting at the root
|
||||
if ($url[0] == '!' || $url[0] == '/') {
|
||||
$url = ltrim($url, '!/');
|
||||
|
||||
if ($file = DauxHelper::getFile($this->daux['tree'], $url)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$triedAbsolute = true;
|
||||
}
|
||||
|
||||
// Seems it's not an absolute path or not found,
|
||||
// so we'll continue with the current folder
|
||||
if ($file = DauxHelper::getFile($this->daux->getCurrentPage()->getParent(), $url)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
// If we didn't already try it, we'll
|
||||
// do a pass starting at the root
|
||||
if (!$triedAbsolute && $file = DauxHelper::getFile($this->daux['tree'], $url)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
throw new LinkNotFoundException("Could not locate file '$url'");
|
||||
}
|
||||
|
||||
protected function isValidUrl($url)
|
||||
{
|
||||
return !empty($url) && $url[0] != '#';
|
||||
}
|
||||
|
||||
protected function isExternalUrl($url)
|
||||
{
|
||||
return preg_match('#^(?:[a-z]+:)?//|^mailto:#', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractInline|Link $inline
|
||||
*
|
||||
* @throws LinkNotFoundException
|
||||
*
|
||||
* @param ElementRendererInterface $htmlRenderer
|
||||
* @return HtmlElement
|
||||
* @throws LinkNotFoundException
|
||||
*/
|
||||
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
|
||||
{
|
||||
if (!($inline instanceof Link)) {
|
||||
throw new \InvalidArgumentException('Incompatible inline type: ' . \get_class($inline));
|
||||
// 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 = $this->parent->render($inline, $htmlRenderer);
|
||||
$element = parent::render($inline, $htmlRenderer);
|
||||
|
||||
$url = $inline->getUrl();
|
||||
|
||||
// empty urls and anchors should
|
||||
// not go through the url resolver
|
||||
if (!DauxHelper::isValidUrl($url)) {
|
||||
if (!$this->isValidUrl($url)) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
// Absolute urls, shouldn't either
|
||||
if (DauxHelper::isExternalUrl($url)) {
|
||||
$element->setAttribute('class', 'Link--external');
|
||||
$element->setAttribute('rel', 'noopener noreferrer');
|
||||
if ($this->isExternalUrl($url)) {
|
||||
$element->setAttribute('class', 'external');
|
||||
|
||||
return $element;
|
||||
}
|
||||
@ -65,35 +109,18 @@ class LinkRenderer implements InlineRendererInterface, ConfigurationAwareInterfa
|
||||
$urlAndHash = explode('#', $url);
|
||||
$url = $urlAndHash[0];
|
||||
|
||||
$foundWithHash = false;
|
||||
|
||||
try {
|
||||
$file = DauxHelper::resolveInternalFile($this->daux, $url);
|
||||
$file = $this->resolveInternalFile($url);
|
||||
$url = DauxHelper::getRelativePath($this->daux->getCurrentPage()->getUrl(), $file->getUrl());
|
||||
} catch (LinkNotFoundException $e) {
|
||||
// For some reason, the filename could contain a # and thus the link needs to resolve to that.
|
||||
try {
|
||||
if (strlen($urlAndHash[1] ?? '') > 0) {
|
||||
$file = DauxHelper::resolveInternalFile($this->daux, $url . '#' . $urlAndHash[1]);
|
||||
$url = DauxHelper::getRelativePath($this->daux->getCurrentPage()->getUrl(), $file->getUrl());
|
||||
$foundWithHash = true;
|
||||
}
|
||||
} catch (LinkNotFoundException $e2) {
|
||||
// If it's still not found here, we'll only
|
||||
// report on the first error as the second
|
||||
// one will tell the same.
|
||||
}
|
||||
|
||||
if (!$foundWithHash) {
|
||||
if ($this->daux->isStatic()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$element->setAttribute('class', 'Link--broken');
|
||||
}
|
||||
$element->setAttribute('class', 'broken');
|
||||
}
|
||||
|
||||
if (!$foundWithHash && isset($urlAndHash[1])) {
|
||||
if (isset($urlAndHash[1])) {
|
||||
$url .= '#' . $urlAndHash[1];
|
||||
}
|
||||
|
||||
@ -101,9 +128,4 @@ class LinkRenderer implements InlineRendererInterface, ConfigurationAwareInterfa
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration)
|
||||
{
|
||||
$this->parent->setConfiguration($configuration);
|
||||
}
|
||||
}
|
||||
|
@ -6,22 +6,43 @@ use League\CommonMark\Cursor;
|
||||
class TableOfContents extends AbstractBlock
|
||||
{
|
||||
/**
|
||||
* Returns true if this block can contain the given block as a child node.
|
||||
* Returns true if this block can contain the given block as a child node
|
||||
*
|
||||
* @param AbstractBlock $block
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canContain(AbstractBlock $block): bool
|
||||
public function canContain(AbstractBlock $block)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a code block.
|
||||
* Returns true if block type can accept lines of text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCode(): bool
|
||||
public function acceptsLines()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function matchesNextLine(Cursor $cursor): bool
|
||||
/**
|
||||
* Whether this is a code block
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCode()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Cursor $cursor
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matchesNextLine(Cursor $cursor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1,19 +1,25 @@
|
||||
<?php namespace Todaymade\Daux\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\Block\Parser\BlockParserInterface;
|
||||
use League\CommonMark\Block\Parser\AbstractBlockParser;
|
||||
use League\CommonMark\ContextInterface;
|
||||
use League\CommonMark\Cursor;
|
||||
|
||||
class TableOfContentsParser implements BlockParserInterface
|
||||
class TableOfContentsParser extends AbstractBlockParser
|
||||
{
|
||||
public function parse(ContextInterface $context, Cursor $cursor): bool
|
||||
/**
|
||||
* @param ContextInterface $context
|
||||
* @param Cursor $cursor
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function parse(ContextInterface $context, Cursor $cursor)
|
||||
{
|
||||
if ($cursor->isIndented()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$previousState = $cursor->saveState();
|
||||
$cursor->advanceToNextNonSpaceOrNewline();
|
||||
$cursor->advanceToFirstNonSpace();
|
||||
$fence = $cursor->match('/^\[TOC\]/');
|
||||
if (is_null($fence)) {
|
||||
$cursor->restoreState($previousState);
|
||||
|
360
libs/Daux.php
360
libs/Daux.php
@ -1,9 +1,10 @@
|
||||
<?php namespace Todaymade\Daux;
|
||||
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
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
|
||||
@ -11,13 +12,11 @@ class Daux
|
||||
const STATIC_MODE = 'DAUX_STATIC';
|
||||
const LIVE_MODE = 'DAUX_LIVE';
|
||||
|
||||
public static $output;
|
||||
/** @var string */
|
||||
public $local_base;
|
||||
|
||||
/** @var Tree\Root */
|
||||
public $tree;
|
||||
|
||||
/** @var Config */
|
||||
public $config;
|
||||
/** @var string */
|
||||
public $internal_base;
|
||||
|
||||
/** @var \Todaymade\Daux\Format\Base\Generator */
|
||||
protected $generator;
|
||||
@ -25,35 +24,221 @@ class Daux
|
||||
/** @var ContentTypeHandler */
|
||||
protected $typeHandler;
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $validExtensions;
|
||||
|
||||
/** @var Processor */
|
||||
protected $processor;
|
||||
|
||||
/** @var Tree\Root */
|
||||
public $tree;
|
||||
|
||||
/** @var Config */
|
||||
public $options;
|
||||
|
||||
/** @var string */
|
||||
private $mode;
|
||||
|
||||
/** @var bool */
|
||||
private $merged_tree = false;
|
||||
|
||||
public function __construct(Config $config, OutputInterface $output)
|
||||
/**
|
||||
* @param string $mode
|
||||
*/
|
||||
public function __construct($mode)
|
||||
{
|
||||
Daux::$output = $output;
|
||||
$this->mode = $mode;
|
||||
|
||||
$this->config = $config;
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the tree that will be used.
|
||||
* @param string $override_file
|
||||
* @throws Exception
|
||||
*/
|
||||
public function initializeConfiguration($override_file = null)
|
||||
{
|
||||
$params = $this->getParams();
|
||||
|
||||
// Validate and set theme path
|
||||
$params->setDocumentationDirectory(
|
||||
$docs_path = $this->normalizeDocumentationPath($this->getParams()->getDocumentationDirectory())
|
||||
);
|
||||
|
||||
// Read documentation overrides
|
||||
$this->loadConfiguration($docs_path . DIRECTORY_SEPARATOR . 'config.json');
|
||||
|
||||
// Read command line overrides
|
||||
$override_file = $this->getConfigurationOverride($override_file);
|
||||
if ($override_file != null) {
|
||||
$params->setConfigurationOverrideFile($override_file);
|
||||
$this->loadConfiguration($override_file);
|
||||
}
|
||||
|
||||
// Validate and set theme path
|
||||
$params->setThemesPath($this->normalizeThemePath($params->getThemesDirectory()));
|
||||
|
||||
// Set a valid default timezone
|
||||
if ($params->hasTimezone()) {
|
||||
date_default_timezone_set($params->getTimezone());
|
||||
} elseif (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('GMT');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file requested for configuration overrides
|
||||
*
|
||||
* @param string|null $override_file
|
||||
* @return string|null the path to a file to load for configuration overrides
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getConfigurationOverride($override_file)
|
||||
{
|
||||
// When running through `daux --serve` we set an environment variable to know where we started from
|
||||
$env = getenv('DAUX_CONFIGURATION');
|
||||
if ($env && file_exists($env)) {
|
||||
return $env;
|
||||
}
|
||||
|
||||
if ($override_file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (file_exists($override_file)) {
|
||||
if (DauxHelper::isAbsolutePath($override_file)) {
|
||||
return $override_file;
|
||||
}
|
||||
|
||||
return getcwd() . '/' . $override_file;
|
||||
}
|
||||
|
||||
$newPath = $this->local_base . DIRECTORY_SEPARATOR . $override_file;
|
||||
if (file_exists($newPath)) {
|
||||
return $newPath;
|
||||
}
|
||||
|
||||
throw new Exception('The configuration override file does not exist. Check the path again : ' . $override_file);
|
||||
}
|
||||
|
||||
public function normalizeThemePath($path)
|
||||
{
|
||||
// When running through `daux --serve` we set an environment variable to know where we started from
|
||||
$env = getenv('DAUX_THEME');
|
||||
if ($env && is_dir($env)) {
|
||||
return $env;
|
||||
}
|
||||
|
||||
if (is_dir($path)) {
|
||||
if (DauxHelper::isAbsolutePath($path)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return getcwd() . '/' . $path;
|
||||
}
|
||||
|
||||
$newPath = $this->local_base . DIRECTORY_SEPARATOR . $path;
|
||||
if (is_dir($newPath)) {
|
||||
return $newPath;
|
||||
}
|
||||
|
||||
throw new Exception('The Themes directory does not exist. Check the path again : ' . $path);
|
||||
}
|
||||
|
||||
public function normalizeDocumentationPath($path)
|
||||
{
|
||||
// When running through `daux --serve` we set an environment variable to know where we started from
|
||||
$env = getenv('DAUX_SOURCE');
|
||||
if ($env && is_dir($env)) {
|
||||
return $env;
|
||||
}
|
||||
|
||||
if (is_dir($path)) {
|
||||
if (DauxHelper::isAbsolutePath($path)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return getcwd() . '/' . $path;
|
||||
}
|
||||
|
||||
throw new Exception('The Docs directory does not exist. Check the path again : ' . $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'],
|
||||
|
||||
//Paths and tree
|
||||
'mode' => $this->mode,
|
||||
'local_base' => $this->local_base,
|
||||
'templates' => 'templates',
|
||||
|
||||
'index_key' => 'index.html',
|
||||
'base_page' => '',
|
||||
'base_url' => '',
|
||||
]);
|
||||
|
||||
// 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->config->setValidContentExtensions($this->getContentExtensions());
|
||||
$this->options['valid_content_extensions'] = $this->getContentExtensions();
|
||||
|
||||
$this->tree = new Root($this->getConfig());
|
||||
Builder::build($this->tree, $this->config->getIgnore());
|
||||
$this->tree = new Root($this->getParams());
|
||||
Builder::build($this->tree, $this->options['ignore']);
|
||||
|
||||
// Apply the language name as Section title
|
||||
if ($this->config->isMultilanguage()) {
|
||||
foreach ($this->config->getLanguages() as $key => $node) {
|
||||
if ($this->options->isMultilanguage()) {
|
||||
foreach ($this->options['languages'] as $key => $node) {
|
||||
$this->tree->getEntries()[$key]->setTitle($node);
|
||||
}
|
||||
}
|
||||
@ -62,43 +247,58 @@ class Daux
|
||||
$this->getProcessor()->manipulateTree($this->tree);
|
||||
|
||||
// Sort the tree one last time before it is finalized
|
||||
Builder::sortTree($this->tree);
|
||||
$this->sortTree($this->tree);
|
||||
|
||||
Builder::finalizeTree($this->tree);
|
||||
$this->finalizeTree($this->tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Config
|
||||
*/
|
||||
public function getConfig()
|
||||
public function sortTree(Directory $current)
|
||||
{
|
||||
if ($this->tree && !$this->merged_tree) {
|
||||
$this->config->setTree($this->tree);
|
||||
$this->config->setIndex($this->tree->getIndexPage() ?: $this->tree->getFirstPage());
|
||||
$entry_page = null;
|
||||
if ($this->config->isMultilanguage()) {
|
||||
$entry_page = [];
|
||||
foreach ($this->config->getLanguages() as $key => $name) {
|
||||
$entry_page[$key] = $this->tree->getEntries()[$key]->getFirstPage();
|
||||
$current->sort();
|
||||
foreach ($current->getEntries() as $entry) {
|
||||
if ($entry instanceof Directory) {
|
||||
$this->sortTree($entry);
|
||||
}
|
||||
} else {
|
||||
$entry_page = $this->tree->getFirstPage();
|
||||
}
|
||||
$this->config->setEntryPage($entry_page);
|
||||
$this->merged_tree = true;
|
||||
}
|
||||
|
||||
return $this->config;
|
||||
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
|
||||
*
|
||||
* @deprecated Use getConfig instead
|
||||
*/
|
||||
public function getParams()
|
||||
{
|
||||
return $this->getConfig();
|
||||
if ($this->tree && !$this->merged_tree) {
|
||||
$this->options['tree'] = $this->tree;
|
||||
$this->options['index'] = $this->tree->getIndexPage() ?: $this->tree->getFirstPage();
|
||||
if ($this->options->isMultilanguage()) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,20 +307,23 @@ class Daux
|
||||
public function getProcessor()
|
||||
{
|
||||
if (!$this->processor) {
|
||||
$this->processor = new Processor($this, Daux::getOutput(), 0);
|
||||
$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
|
||||
// the best i've found to use the
|
||||
// processor in very remote places
|
||||
$this->config->setProcessorInstance($processor);
|
||||
$this->options['processor_instance'] = $processor;
|
||||
}
|
||||
|
||||
public function getGenerators()
|
||||
@ -136,9 +339,10 @@ class Daux
|
||||
return array_replace($default, $extended);
|
||||
}
|
||||
|
||||
|
||||
public function getProcessorClass()
|
||||
{
|
||||
$processor = $this->getConfig()->getProcessor();
|
||||
$processor = $this->getParams()['processor'];
|
||||
|
||||
if (empty($processor)) {
|
||||
return null;
|
||||
@ -156,21 +360,6 @@ class Daux
|
||||
return $class;
|
||||
}
|
||||
|
||||
protected function findAlternatives($input, $words)
|
||||
{
|
||||
$alternatives = [];
|
||||
|
||||
foreach ($words as $word) {
|
||||
$lev = levenshtein($input, $word);
|
||||
|
||||
if ($lev <= \strlen($word) / 3) {
|
||||
$alternatives[] = $word;
|
||||
}
|
||||
}
|
||||
|
||||
return $alternatives;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Todaymade\Daux\Format\Base\Generator
|
||||
*/
|
||||
@ -182,22 +371,10 @@ class Daux
|
||||
|
||||
$generators = $this->getGenerators();
|
||||
|
||||
$format = $this->getConfig()->getFormat();
|
||||
$format = $this->getParams()->getFormat();
|
||||
|
||||
if (!array_key_exists($format, $generators)) {
|
||||
$message = "The format '$format' doesn't exist, did you forget to set your processor ?";
|
||||
|
||||
$alternatives = $this->findAlternatives($format, array_keys($generators));
|
||||
|
||||
if (0 == \count($alternatives)) {
|
||||
$message .= "\n\nAvailable formats are \n " . implode("\n ", array_keys($generators));
|
||||
} elseif (1 == \count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n " . implode("\n ", $alternatives);
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n " . implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \RuntimeException($message);
|
||||
throw new \RuntimeException("The format '$format' doesn't exist, did you forget to set your processor ?");
|
||||
}
|
||||
|
||||
$class = $generators[$format];
|
||||
@ -229,7 +406,7 @@ class Daux
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all content file extensions.
|
||||
* Get all content file extensions
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
@ -241,41 +418,4 @@ class Daux
|
||||
|
||||
return $this->validExtensions = $this->getContentTypeHandler()->getContentExtensions();
|
||||
}
|
||||
|
||||
public static function getOutput()
|
||||
{
|
||||
if (!Daux::$output) {
|
||||
Daux::$output = new NullOutput();
|
||||
}
|
||||
|
||||
return Daux::$output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message to the output.
|
||||
*
|
||||
* @param array|string $messages The message as an array of lines or a single string
|
||||
* @param bool $newline Whether to add a newline
|
||||
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
|
||||
*/
|
||||
public static function write($messages, $newline = false, $options = 0)
|
||||
{
|
||||
Daux::getOutput()->write($messages, $newline, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message to the output and adds a newline at the end.
|
||||
*
|
||||
* @param array|string $messages The message as an array of lines of a single string
|
||||
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
|
||||
*/
|
||||
public static function writeln($messages, $options = 0)
|
||||
{
|
||||
Daux::getOutput()->write($messages, true, $options);
|
||||
}
|
||||
|
||||
public static function getVerbosity()
|
||||
{
|
||||
return Daux::getOutput()->getVerbosity();
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +1,65 @@
|
||||
<?php namespace Todaymade\Daux;
|
||||
|
||||
use Todaymade\Daux\Exception\LinkNotFoundException;
|
||||
use Todaymade\Daux\Tree\Builder;
|
||||
use Todaymade\Daux\Tree\Directory;
|
||||
use Todaymade\Daux\Tree\Entry;
|
||||
|
||||
class DauxHelper
|
||||
{
|
||||
/**
|
||||
* Set a new base_url for the configuration.
|
||||
* 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->getBaseUrl() == $base_url && !empty($config->getTheme())) {
|
||||
if ($config['base_url'] == $base_url && !empty($config['theme'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Change base url for all links on the pages
|
||||
$config['base_url'] = $base_url;
|
||||
$config['base_url'] = $config['base_page'] = $base_url;
|
||||
$config['theme'] = static::getTheme($config, $base_url);
|
||||
$config['image'] = str_replace('<base_url>', $base_url, $config->getImage());
|
||||
$config['image'] = str_replace('<base_url>', $base_url, $config['image']);
|
||||
}
|
||||
|
||||
protected static function resolveVariant(Config $params)
|
||||
{
|
||||
if (array_key_exists('theme-variant', $params['html'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_dir(realpath(($params->getThemesPath() . DIRECTORY_SEPARATOR . $params['html']['theme'])))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$theme = explode('-', $params['html']['theme']);
|
||||
|
||||
// do we have a variant or only a theme ?
|
||||
if (isset($theme[1])) {
|
||||
$params['html']['theme-variant'] = array_pop($theme);
|
||||
$params['html']['theme'] = implode('-', $theme);
|
||||
} else {
|
||||
$params['html']['theme'] = array_pop($theme);
|
||||
}
|
||||
|
||||
if (!is_dir(realpath($params->getThemesPath() . DIRECTORY_SEPARATOR . $params['html']['theme']))) {
|
||||
throw new \RuntimeException("Theme '{$params['html']['theme']}' not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $params
|
||||
* @param string $current_url
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function getTheme(Config $config, $current_url)
|
||||
protected static function getTheme(Config $params, $current_url)
|
||||
{
|
||||
static $cache = [];
|
||||
self::resolveVariant($params);
|
||||
|
||||
$htmlTheme = $config->getHTML()->getTheme();
|
||||
|
||||
$theme_folder = $config->getThemesPath() . DIRECTORY_SEPARATOR . $htmlTheme;
|
||||
$theme_url = $config->getBaseUrl() . 'themes/' . $htmlTheme . '/';
|
||||
|
||||
$cache_key = "$current_url-$htmlTheme";
|
||||
if (array_key_exists($cache_key, $cache)) {
|
||||
return $cache[$cache_key];
|
||||
}
|
||||
$theme_folder = $params->getThemesPath() . DIRECTORY_SEPARATOR . $params['html']['theme'];
|
||||
$theme_url = $params['base_url'] . 'themes/' . $params['html']['theme'] . '/';
|
||||
|
||||
$theme = [];
|
||||
if (is_file($theme_folder . DIRECTORY_SEPARATOR . 'config.json')) {
|
||||
@ -52,20 +69,19 @@ class DauxHelper
|
||||
}
|
||||
}
|
||||
|
||||
// Default parameters for theme
|
||||
//Default parameters for theme
|
||||
$theme += [
|
||||
'name' => $htmlTheme,
|
||||
'name' => $params['html']['theme'],
|
||||
'css' => [],
|
||||
'js' => [],
|
||||
'fonts' => [],
|
||||
'favicon' => '<base_url>themes/daux/img/favicon.png',
|
||||
'templates' => $theme_folder . DIRECTORY_SEPARATOR . 'templates',
|
||||
'variants' => [],
|
||||
'with_search' => $config->getHTML()->hasSearch()
|
||||
];
|
||||
|
||||
if ($config->getHTML()->hasThemeVariant()) {
|
||||
$variant = $config->getHTML()->getThemeVariant();
|
||||
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]'");
|
||||
}
|
||||
@ -85,16 +101,8 @@ class DauxHelper
|
||||
}
|
||||
}
|
||||
|
||||
if ($theme['with_search']) {
|
||||
$theme['css'][] = '<base_url>daux_libraries/search.css';
|
||||
}
|
||||
|
||||
if (is_file($config->getDocumentationDirectory() . DIRECTORY_SEPARATOR . 'style.css')) {
|
||||
$theme['css'][]= '<base_url>style.css';
|
||||
}
|
||||
|
||||
$substitutions = [
|
||||
'<local_base>' => $config->getLocalBase(),
|
||||
'<local_base>' => $params['local_base'],
|
||||
'<base_url>' => $current_url,
|
||||
'<theme_url>' => $theme_url,
|
||||
];
|
||||
@ -109,16 +117,13 @@ class DauxHelper
|
||||
}
|
||||
}
|
||||
|
||||
$cache[$cache_key] = $theme;
|
||||
|
||||
return $theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all '/./' and '/../' in a path, without actually checking the path.
|
||||
* Remove all '/./' and '/../' in a path, without actually checking the path
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCleanPath($path)
|
||||
@ -143,13 +148,13 @@ class DauxHelper
|
||||
/**
|
||||
* Get the possible output file names for a source file.
|
||||
*
|
||||
* @param Config $config
|
||||
* @param string $part
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getFilenames(Config $config, $part)
|
||||
{
|
||||
$extensions = implode('|', array_map('preg_quote', $config->getValidContentExtensions())) . '|html';
|
||||
$extensions = implode('|', array_map('preg_quote', $config['valid_content_extensions'])) . '|html';
|
||||
|
||||
$raw = preg_replace('/(.*)?\\.(' . $extensions . ')$/', '$1', $part);
|
||||
$raw = Builder::removeSortingInformations($raw);
|
||||
@ -158,12 +163,11 @@ class DauxHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a file in the tree. Returns the file if found or false.
|
||||
* Locate a file in the tree. Returns the file if found or false
|
||||
*
|
||||
* @param Directory $tree
|
||||
* @param string $request
|
||||
*
|
||||
* @return false|Tree\Content|Tree\Raw
|
||||
* @return Tree\Content|Tree\Raw|false
|
||||
*/
|
||||
public static function getFile($tree, $request)
|
||||
{
|
||||
@ -182,24 +186,16 @@ class DauxHelper
|
||||
|
||||
if ($node == '..') {
|
||||
$tree = $tree->getParent();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$node = urldecode($node);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// We try a second time by decoding the url
|
||||
$node = DauxHelper::slug(urldecode($node));
|
||||
if (isset($tree->getEntries()[$node])) {
|
||||
$tree = $tree->getEntries()[$node];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -209,7 +205,6 @@ class DauxHelper
|
||||
foreach (static::getFilenames($tree->getConfig(), $node) as $filename) {
|
||||
if (isset($tree->getEntries()[$filename])) {
|
||||
$tree = $tree->getEntries()[$filename];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
@ -242,28 +237,22 @@ class DauxHelper
|
||||
* Taken from Stringy
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function slug($title)
|
||||
{
|
||||
// Convert to ASCII
|
||||
if (function_exists('transliterator_transliterate')) {
|
||||
$title = transliterator_transliterate('Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC;', $title);
|
||||
foreach (static::charsArray() as $key => $value) {
|
||||
$title = str_replace($value, $key, $title);
|
||||
}
|
||||
|
||||
$title = iconv('utf-8', 'ASCII//TRANSLIT//IGNORE', $title);
|
||||
|
||||
// Remove unsupported characters
|
||||
$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 valid in a URL:
|
||||
// $-_.+!*'(), separator, letters, numbers, or whitespace.
|
||||
$title = preg_replace('![^-' . preg_quote($separator) . '\!\'\(\),\.\+\*\$\pL\pN\s]+!u', '', $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);
|
||||
@ -271,10 +260,149 @@ class DauxHelper
|
||||
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 = [
|
||||
'a' => [
|
||||
'à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ',
|
||||
'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ä', 'ā', 'ą',
|
||||
'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ',
|
||||
'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ',
|
||||
'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', ],
|
||||
'b' => ['б', 'β', 'Ъ', 'Ь', 'ب'],
|
||||
'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ'],
|
||||
'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ',
|
||||
'д', 'δ', 'د', 'ض', ],
|
||||
'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ',
|
||||
'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ',
|
||||
'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э',
|
||||
'є', 'ə', ],
|
||||
'f' => ['ф', 'φ', 'ف'],
|
||||
'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ج'],
|
||||
'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه'],
|
||||
'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į',
|
||||
'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ',
|
||||
'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ',
|
||||
'ῗ', 'і', 'ї', 'и', ],
|
||||
'j' => ['ĵ', 'ј', 'Ј'],
|
||||
'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك'],
|
||||
'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل'],
|
||||
'm' => ['м', 'μ', 'م'],
|
||||
'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن'],
|
||||
'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ',
|
||||
'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő',
|
||||
'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό',
|
||||
'ö', 'о', 'و', 'θ', ],
|
||||
'p' => ['п', 'π'],
|
||||
'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر'],
|
||||
's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص'],
|
||||
't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط'],
|
||||
'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ',
|
||||
'ự', 'ü', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', ],
|
||||
'v' => ['в'],
|
||||
'w' => ['ŵ', 'ω', 'ώ'],
|
||||
'x' => ['χ'],
|
||||
'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ',
|
||||
'ϋ', 'ύ', 'ΰ', 'ي', ],
|
||||
'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز'],
|
||||
'aa' => ['ع'],
|
||||
'ae' => ['æ'],
|
||||
'ch' => ['ч'],
|
||||
'dj' => ['ђ', 'đ'],
|
||||
'dz' => ['џ'],
|
||||
'gh' => ['غ'],
|
||||
'kh' => ['х', 'خ'],
|
||||
'lj' => ['љ'],
|
||||
'nj' => ['њ'],
|
||||
'oe' => ['œ'],
|
||||
'ps' => ['ψ'],
|
||||
'sh' => ['ш'],
|
||||
'shch' => ['щ'],
|
||||
'ss' => ['ß'],
|
||||
'th' => ['þ', 'ث', 'ذ', 'ظ'],
|
||||
'ts' => ['ц'],
|
||||
'ya' => ['я'],
|
||||
'yu' => ['ю'],
|
||||
'zh' => ['ж'],
|
||||
'(c)' => ['©'],
|
||||
'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ',
|
||||
'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Ä', 'Å', 'Ā',
|
||||
'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ',
|
||||
'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ',
|
||||
'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', ],
|
||||
'B' => ['Б', 'Β'],
|
||||
'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ'],
|
||||
'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ'],
|
||||
'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ',
|
||||
'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ',
|
||||
'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э',
|
||||
'Є', 'Ə', ],
|
||||
'F' => ['Ф', 'Φ'],
|
||||
'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ'],
|
||||
'H' => ['Η', 'Ή'],
|
||||
'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į',
|
||||
'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ',
|
||||
'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', ],
|
||||
'K' => ['К', 'Κ'],
|
||||
'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ'],
|
||||
'M' => ['М', 'Μ'],
|
||||
'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν'],
|
||||
'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ',
|
||||
'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ö', 'Ø', 'Ō',
|
||||
'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ',
|
||||
'Ὸ', 'Ό', 'О', 'Θ', 'Ө', ],
|
||||
'P' => ['П', 'Π'],
|
||||
'R' => ['Ř', 'Ŕ', 'Р', 'Ρ'],
|
||||
'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ'],
|
||||
'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ'],
|
||||
'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ',
|
||||
'Ự', 'Û', 'Ü', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', ],
|
||||
'V' => ['В'],
|
||||
'W' => ['Ω', 'Ώ'],
|
||||
'X' => ['Χ'],
|
||||
'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ',
|
||||
'Ы', 'Й', 'Υ', 'Ϋ', ],
|
||||
'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ'],
|
||||
'AE' => ['Æ'],
|
||||
'CH' => ['Ч'],
|
||||
'DJ' => ['Ђ'],
|
||||
'DZ' => ['Џ'],
|
||||
'KH' => ['Х'],
|
||||
'LJ' => ['Љ'],
|
||||
'NJ' => ['Њ'],
|
||||
'PS' => ['Ψ'],
|
||||
'SH' => ['Ш'],
|
||||
'SHCH' => ['Щ'],
|
||||
'SS' => ['ẞ'],
|
||||
'TH' => ['Þ'],
|
||||
'TS' => ['Ц'],
|
||||
'YA' => ['Я'],
|
||||
'YU' => ['Ю'],
|
||||
'ZH' => ['Ж'],
|
||||
' ' => ["\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", ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getRelativePath($from, $to)
|
||||
@ -301,12 +429,12 @@ class DauxHelper
|
||||
// add traversals up to first matching dir
|
||||
$padLength = (count($relPath) + $remaining - 1) * -1;
|
||||
$relPath = array_pad($relPath, $padLength, '..');
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
//$relPath[0] = './' . $relPath[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode('/', $relPath);
|
||||
}
|
||||
@ -315,13 +443,11 @@ class DauxHelper
|
||||
{
|
||||
if (!is_string($path)) {
|
||||
$mess = sprintf('String expected but was given %s', gettype($path));
|
||||
|
||||
throw new \InvalidArgumentException($mess);
|
||||
}
|
||||
|
||||
if (!ctype_print($path)) {
|
||||
$mess = 'Path can NOT have non-printable characters or be empty';
|
||||
|
||||
throw new \DomainException($mess);
|
||||
}
|
||||
|
||||
@ -337,74 +463,9 @@ class DauxHelper
|
||||
$parts = [];
|
||||
if (!preg_match($regExp, $path, $parts)) {
|
||||
$mess = sprintf('Path is NOT valid, was given %s', $path);
|
||||
|
||||
throw new \DomainException($mess);
|
||||
}
|
||||
|
||||
return '' !== $parts['root'];
|
||||
}
|
||||
|
||||
public static function getAbsolutePath($path)
|
||||
{
|
||||
if (DauxHelper::isAbsolutePath($path)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return getcwd() . '/' . $path;
|
||||
}
|
||||
|
||||
public static function is($path, $type)
|
||||
{
|
||||
return ($type == 'dir') ? is_dir($path) : file_exists($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $config
|
||||
* @param string $url
|
||||
*
|
||||
* @throws LinkNotFoundException
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
public static function resolveInternalFile($config, $url)
|
||||
{
|
||||
$triedAbsolute = false;
|
||||
|
||||
// Legacy absolute paths could start with
|
||||
// "!" In this case we will try to find
|
||||
// the file starting at the root
|
||||
if ($url[0] == '!' || $url[0] == '/') {
|
||||
$url = ltrim($url, '!/');
|
||||
|
||||
if ($file = DauxHelper::getFile($config->getTree(), $url)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$triedAbsolute = true;
|
||||
}
|
||||
|
||||
// Seems it's not an absolute path or not found,
|
||||
// so we'll continue with the current folder
|
||||
if ($file = DauxHelper::getFile($config->getCurrentPage()->getParent(), $url)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
// If we didn't already try it, we'll
|
||||
// do a pass starting at the root
|
||||
if (!$triedAbsolute && $file = DauxHelper::getFile($config->getTree(), $url)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
throw new LinkNotFoundException("Could not locate file '$url'");
|
||||
}
|
||||
|
||||
public static function isValidUrl($url)
|
||||
{
|
||||
return !empty($url) && $url[0] != '#';
|
||||
}
|
||||
|
||||
public static function isExternalUrl($url)
|
||||
{
|
||||
return preg_match('#^(?:[a-z]+:)?//|^mailto:#', $url);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ abstract class ContentPage extends SimplePage
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* @var ContentType
|
||||
@ -38,17 +38,9 @@ abstract class ContentPage extends SimplePage
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function setConfig(Config $config)
|
||||
public function setParams(Config $params)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use setConfig instead
|
||||
*/
|
||||
public function setParams(Config $config)
|
||||
{
|
||||
$this->setConfig($config);
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,11 +65,11 @@ abstract class ContentPage extends SimplePage
|
||||
return $this->getPureContent();
|
||||
}
|
||||
|
||||
public static function fromFile(Content $file, $config, ContentType $contentType)
|
||||
public static function fromFile(Content $file, $params, ContentType $contentType)
|
||||
{
|
||||
$page = new static($file->getTitle(), $file->getContent());
|
||||
$page->setFile($file);
|
||||
$page->setConfig($config);
|
||||
$page->setParams($params);
|
||||
$page->setContentType($contentType);
|
||||
|
||||
return $page;
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: onigoetz
|
||||
* Date: 06/11/15
|
||||
* Time: 20:27.
|
||||
* Time: 20:27
|
||||
*/
|
||||
namespace Todaymade\Daux\Format\Base;
|
||||
|
||||
@ -67,6 +67,7 @@ class EmbedImages
|
||||
//Get any file corresponding to the right one
|
||||
$file = DauxHelper::getFile($this->tree, $url);
|
||||
|
||||
|
||||
if ($file === false) {
|
||||
return false;
|
||||
}
|
||||
|
@ -6,11 +6,15 @@ use Todaymade\Daux\Daux;
|
||||
|
||||
interface Generator
|
||||
{
|
||||
/**
|
||||
* @param Daux $daux
|
||||
*/
|
||||
public function __construct(Daux $daux);
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @param int $width
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function generateAll(InputInterface $input, OutputInterface $output, $width);
|
||||
|
@ -6,7 +6,9 @@ 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 $config);
|
||||
public function generateOne(Entry $node, Config $params);
|
||||
}
|
||||
|
@ -3,14 +3,14 @@
|
||||
interface Page
|
||||
{
|
||||
/**
|
||||
* Get the converted content, without any template.
|
||||
* Get the converted content, without any template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPureContent();
|
||||
|
||||
/**
|
||||
* Get the full content.
|
||||
* Get the full content
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
|
@ -24,8 +24,7 @@ class Api
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is public due to test purposes.
|
||||
*
|
||||
* This method is public due to test purposes
|
||||
* @return Client
|
||||
*/
|
||||
public function getClient()
|
||||
@ -40,8 +39,9 @@ class Api
|
||||
|
||||
/**
|
||||
* The standard error message from guzzle is quite poor in informations,
|
||||
* this will give little bit more sense to it and return it.
|
||||
* this will give little bit more sense to it and return it
|
||||
*
|
||||
* @param BadResponseException $e
|
||||
* @return \Exception
|
||||
*/
|
||||
protected function handleError(BadResponseException $e)
|
||||
@ -60,9 +60,9 @@ class Api
|
||||
}
|
||||
|
||||
$message = $label .
|
||||
"\n [url] " . $request->getUri() .
|
||||
"\n [status code] " . $response->getStatusCode() .
|
||||
"\n [message] ";
|
||||
' [url] ' . $request->getUri() .
|
||||
' [status code] ' . $response->getStatusCode() .
|
||||
' [message] ';
|
||||
|
||||
$body = $response->getBody();
|
||||
$json = json_decode($body, true);
|
||||
@ -101,11 +101,10 @@ class Api
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of pages.
|
||||
* Get a list of pages
|
||||
*
|
||||
* @param int $rootPage
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getList($rootPage, $recursive = false)
|
||||
@ -154,7 +153,6 @@ class Api
|
||||
* @param int $parent_id
|
||||
* @param string $title
|
||||
* @param string $content
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function createPage($parent_id, $title, $content)
|
||||
@ -203,56 +201,14 @@ class Api
|
||||
try {
|
||||
$this->getClient()->put("content/$page_id", ['json' => $body]);
|
||||
} catch (BadResponseException $e) {
|
||||
$error = $this->handleError($e);
|
||||
|
||||
$re = '/\[([0-9]*),([0-9]*)\]$/';
|
||||
preg_match($re, $error->getMessage(), $matches, PREG_OFFSET_CAPTURE, 0);
|
||||
|
||||
if (count($matches) == 3) {
|
||||
echo "\nContent: \n";
|
||||
echo $this->showSourceCode($content, $matches[1][0], $matches[2][0]);
|
||||
throw $this->handleError($e);
|
||||
}
|
||||
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
public function showSourceCode($css, $lineNumber, $column)
|
||||
{
|
||||
$lines = preg_split("/\r?\n/", $css);
|
||||
|
||||
if ($lines === false) {
|
||||
return $css;
|
||||
}
|
||||
|
||||
$start = max($lineNumber - 3, 0);
|
||||
$end = min($lineNumber + 2, count($lines));
|
||||
|
||||
$maxWidth = strlen("$end");
|
||||
|
||||
$filtered = array_slice($lines, $start, $end - $start);
|
||||
|
||||
$prepared = [];
|
||||
foreach ($filtered as $index => $line) {
|
||||
$number = $start + 1 + $index;
|
||||
$gutter = substr(' ' . (' ' . $number), -$maxWidth) . ' | ';
|
||||
|
||||
if ($number == $lineNumber) {
|
||||
$spacing = str_repeat(' ', strlen($gutter) + $column - 2);
|
||||
$prepared[] = '>' . $gutter . $line . "\n " . $spacing . '^';
|
||||
} else {
|
||||
$prepared[] = ' ' . $gutter . $line;
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $prepared);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a page.
|
||||
* Delete a page
|
||||
*
|
||||
* @param int $page_id
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function deletePage($page_id)
|
||||
@ -264,21 +220,29 @@ class Api
|
||||
}
|
||||
}
|
||||
|
||||
private function getAttachment($id, $attachment)
|
||||
/**
|
||||
* @param int $id
|
||||
* @param array $attachment
|
||||
*/
|
||||
public function uploadAttachment($id, $attachment)
|
||||
{
|
||||
// Check if an attachment with
|
||||
// this name is uploaded
|
||||
try {
|
||||
$url = "content/$id/child/attachment?filename=" . urlencode($attachment['filename']);
|
||||
|
||||
return json_decode($this->getClient()->get($url)->getBody(), true);
|
||||
$result = json_decode($this->getClient()->get($url)->getBody(), true);
|
||||
} 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";
|
||||
}
|
||||
|
||||
private function putAttachment($url, $attachment)
|
||||
{
|
||||
$contents = array_key_exists('file', $attachment) ? fopen($attachment['file']->getPath(), 'r') : $attachment['content'];
|
||||
|
||||
try {
|
||||
@ -293,43 +257,4 @@ class Api
|
||||
throw $this->handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
private function getFileSize($attachment)
|
||||
{
|
||||
if (array_key_exists('file', $attachment)) {
|
||||
return filesize($attachment['file']->getPath());
|
||||
}
|
||||
|
||||
if (function_exists('mb_strlen')) {
|
||||
return mb_strlen($attachment['content']);
|
||||
}
|
||||
|
||||
return strlen($attachment['content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param array $attachment
|
||||
* @param callback $write Write output to the console
|
||||
*/
|
||||
public function uploadAttachment($id, $attachment, $write)
|
||||
{
|
||||
$result = $this->getAttachment($id, $attachment);
|
||||
|
||||
$url = "content/$id/child/attachment";
|
||||
|
||||
// If the attachment is already uploaded,
|
||||
// the update URL is different
|
||||
if (count($result['results'])) {
|
||||
if ($this->getFileSize($attachment) == $result['results'][0]['extensions']['fileSize']) {
|
||||
$write(' ( An attachment of the same size already exists, skipping. )');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$url .= "/{$result['results'][0]['id']}/data";
|
||||
}
|
||||
|
||||
$this->putAttachment($url, $attachment);
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\Confluence;
|
||||
|
||||
use Todaymade\Daux\BaseConfig;
|
||||
|
||||
class Config extends BaseConfig
|
||||
{
|
||||
public function shouldAutoDeleteOrphanedPages()
|
||||
{
|
||||
if ($this->hasValue('delete')) {
|
||||
return $this->getValue('delete');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUpdateThreshold()
|
||||
{
|
||||
return $this->hasValue('update_threshold') ? $this->getValue('update_threshold') : 2;
|
||||
}
|
||||
|
||||
public function getPrefix()
|
||||
{
|
||||
return $this->getValue('prefix');
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->getValue('base_url');
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
return $this->getValue('user');
|
||||
}
|
||||
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->getValue('pass');
|
||||
}
|
||||
|
||||
public function getSpaceId()
|
||||
{
|
||||
return $this->getValue('space_id');
|
||||
}
|
||||
|
||||
public function hasAncestorId()
|
||||
{
|
||||
return $this->hasValue('ancestor_id');
|
||||
}
|
||||
|
||||
public function getAncestorId()
|
||||
{
|
||||
return $this->getValue('ancestor_id');
|
||||
}
|
||||
|
||||
public function setAncestorId($value)
|
||||
{
|
||||
$this->setValue('ancestor_id', $value);
|
||||
}
|
||||
|
||||
public function hasRootId()
|
||||
{
|
||||
return $this->hasValue('root_id');
|
||||
}
|
||||
|
||||
public function getRootId()
|
||||
{
|
||||
return $this->getValue('root_id');
|
||||
}
|
||||
|
||||
public function hasHeader()
|
||||
{
|
||||
return $this->hasValue('header');
|
||||
}
|
||||
|
||||
public function getHeader()
|
||||
{
|
||||
return $this->getValue('header');
|
||||
}
|
||||
}
|
@ -15,40 +15,39 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
|
||||
//Embed images
|
||||
// We do it after generation so we can catch the images that were in html already
|
||||
$content = (new EmbedImages($this->config->getTree()))
|
||||
$content = (new EmbedImages($this->params['tree']))
|
||||
->embed(
|
||||
$content,
|
||||
$this->file,
|
||||
function ($src, array $attributes, Entry $file) {
|
||||
|
||||
//Add the attachment for later upload
|
||||
if ($file instanceof Raw) {
|
||||
$filename = basename($file->getPath());
|
||||
$this->attachments[$filename] = ['filename' => $filename, 'file' => $file];
|
||||
} elseif ($file instanceof ComputedRaw) {
|
||||
} else if ($file instanceof ComputedRaw) {
|
||||
$filename = $file->getUri();
|
||||
$this->attachments[$filename] = ['filename' => $filename, 'content' => $file->getContent()];
|
||||
} else {
|
||||
throw new \RuntimeException("Cannot embed image as we don't understand its type.");
|
||||
}
|
||||
|
||||
return $this->createImageTag($filename, $attributes);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$intro = '';
|
||||
if ($this->config->getConfluenceConfiguration()->hasHeader()) {
|
||||
$intro = '<ac:structured-macro ac:name="info"><ac:rich-text-body>' . $this->config->getConfluenceConfiguration()->getHeader() . '</ac:rich-text-body></ac:structured-macro>';
|
||||
if (array_key_exists('confluence', $this->params) && array_key_exists('header', $this->params['confluence']) && !empty($this->params['confluence']['header'])) {
|
||||
$intro = '<ac:structured-macro ac:name="info"><ac:rich-text-body>' . $this->params['confluence']['header'] . '</ac:rich-text-body></ac:structured-macro>';
|
||||
}
|
||||
|
||||
return $intro . $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an image tag for the specified filename.
|
||||
* Create an image tag for the specified filename
|
||||
*
|
||||
* @param string $filename
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function createImageTag($filename, $attributes)
|
||||
@ -60,7 +59,7 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
$re = '/float:\s*?(left|right);?/';
|
||||
if (preg_match($re, $value, $matches)) {
|
||||
$img .= ' ac:align="' . $matches[1] . '"';
|
||||
$value = preg_replace($re, '', $value, 1);
|
||||
$value = preg_replace($re, "", $value, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\Block\Renderer\BlockRendererInterface;
|
||||
use League\CommonMark\HtmlElement;
|
||||
|
||||
abstract class CodeRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function escapeCDATA($content)
|
||||
{
|
||||
return str_replace(']]>', ']]]]><![CDATA[>', $content);
|
||||
}
|
||||
|
||||
public function getHTMLElement($body, $language)
|
||||
{
|
||||
$body = '<![CDATA[' . $this->escapeCDATA($body) . ']]>';
|
||||
|
||||
$content = [];
|
||||
|
||||
if ($language) {
|
||||
$content[] = new HtmlElement('ac:parameter', ['ac:name' => 'language'], $language);
|
||||
}
|
||||
|
||||
$content[] = new HtmlElement('ac:plain-text-body', [], $body);
|
||||
|
||||
return new HtmlElement(
|
||||
'ac:structured-macro',
|
||||
['ac:name' => 'code'],
|
||||
$content
|
||||
);
|
||||
}
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\Block\Element as BlockElement;
|
||||
use League\CommonMark\Environment;
|
||||
use League\CommonMark\Inline\Element as InlineElement;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
|
||||
|
||||
class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter
|
||||
{
|
||||
@ -17,12 +14,12 @@ class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMa
|
||||
{
|
||||
parent::extendEnvironment($environment, $config);
|
||||
|
||||
$environment->addBlockRenderer(TableOfContents::class, new TOCRenderer());
|
||||
$environment->addBlockRenderer('Todaymade\Daux\ContentTypes\Markdown\TableOfContents', new TOCRenderer());
|
||||
|
||||
//Add code renderer
|
||||
$environment->addBlockRenderer(BlockElement\FencedCode::class, new FencedCodeRenderer());
|
||||
$environment->addBlockRenderer(BlockElement\IndentedCode::class, new IndentedCodeRenderer());
|
||||
$environment->addBlockRenderer('FencedCode', new FencedCodeRenderer());
|
||||
$environment->addBlockRenderer('IndentedCode', new IndentedCodeRenderer());
|
||||
|
||||
$environment->addInlineRenderer(InlineElement\Image::class, new ImageRenderer());
|
||||
$environment->addInlineRenderer('Image', new ImageRenderer());
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
|
||||
|
||||
use Todaymade\Daux\Config;
|
||||
|
||||
class ContentType extends \Todaymade\Daux\ContentTypes\Markdown\ContentType
|
||||
{
|
||||
protected function createConverter()
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
return new CommonMarkConverter(['daux' => $this->config]);
|
||||
$this->config = $config;
|
||||
$this->converter = new CommonMarkConverter(['daux' => $config]);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
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;
|
||||
use League\CommonMark\Util\Xml;
|
||||
|
||||
class FencedCodeRenderer extends CodeRenderer
|
||||
class FencedCodeRenderer implements BlockRendererInterface
|
||||
{
|
||||
protected $supported_languages = [
|
||||
'actionscript3',
|
||||
@ -36,6 +36,8 @@ class FencedCodeRenderer extends CodeRenderer
|
||||
protected $known_conversions = ['html' => 'html/xml', 'xml' => 'html/xml', 'js' => 'javascript'];
|
||||
|
||||
/**
|
||||
* @param AbstractBlock $block
|
||||
* @param HtmlRendererInterface $htmlRenderer
|
||||
* @param bool $inTightList
|
||||
*
|
||||
* @return HtmlElement|string
|
||||
@ -46,18 +48,28 @@ class FencedCodeRenderer extends CodeRenderer
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
$language = $this->getLanguage($block->getInfoWords());
|
||||
$content = [];
|
||||
|
||||
return $this->getHTMLElement($block->getStringContent(), $language);
|
||||
if ($language = $this->getLanguage($block->getInfoWords(), $htmlRenderer)) {
|
||||
$content[] = new HtmlElement('ac:parameter', ['ac:name' => 'language'], $language);
|
||||
}
|
||||
|
||||
public function getLanguage($infoWords)
|
||||
$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 = Xml::escape($infoWords[0]);
|
||||
$language = $htmlRenderer->escape($infoWords[0], true);
|
||||
|
||||
if (array_key_exists($language, $this->known_conversions)) {
|
||||
$language = $this->known_conversions[$language];
|
||||
|
@ -4,29 +4,12 @@ use League\CommonMark\ElementRendererInterface;
|
||||
use League\CommonMark\HtmlElement;
|
||||
use League\CommonMark\Inline\Element\AbstractInline;
|
||||
use League\CommonMark\Inline\Element\Image;
|
||||
use League\CommonMark\Inline\Renderer\InlineRendererInterface;
|
||||
use League\CommonMark\Util\ConfigurationAwareInterface;
|
||||
use League\CommonMark\Util\ConfigurationInterface;
|
||||
|
||||
class ImageRenderer implements InlineRendererInterface, ConfigurationAwareInterface
|
||||
class ImageRenderer extends \League\CommonMark\Inline\Renderer\ImageRenderer
|
||||
{
|
||||
/**
|
||||
* @var ConfigurationInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \League\CommonMark\Inline\Renderer\ImageRenderer
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->parent = new \League\CommonMark\Inline\Renderer\ImageRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Image $inline
|
||||
* @param ElementRendererInterface $htmlRenderer
|
||||
*
|
||||
* @return HtmlElement
|
||||
*/
|
||||
@ -45,11 +28,6 @@ class ImageRenderer implements InlineRendererInterface, ConfigurationAwareInterf
|
||||
);
|
||||
}
|
||||
|
||||
return $this->parent->render($inline, $htmlRenderer);
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration)
|
||||
{
|
||||
$this->parent->setConfiguration($configuration);
|
||||
return parent::render($inline, $htmlRenderer);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,15 @@
|
||||
|
||||
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 extends CodeRenderer
|
||||
class IndentedCodeRenderer implements BlockRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param AbstractBlock $block
|
||||
* @param HtmlRendererInterface $htmlRenderer
|
||||
* @param bool $inTightList
|
||||
*
|
||||
* @return HtmlElement
|
||||
@ -18,6 +21,10 @@ class IndentedCodeRenderer extends CodeRenderer
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
return $this->getHTMLElement($block->getStringContent(), '');
|
||||
return new HtmlElement(
|
||||
'ac:structured-macro',
|
||||
['ac:name' => 'code'],
|
||||
new HtmlElement('ac:plain-text-body', [], '<![CDATA[' . $block->getStringContent() . ']]>')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,19 +4,26 @@ use League\CommonMark\ElementRendererInterface;
|
||||
use League\CommonMark\HtmlElement;
|
||||
use League\CommonMark\Inline\Element\AbstractInline;
|
||||
use League\CommonMark\Inline\Element\Link;
|
||||
use Todaymade\Daux\DauxHelper;
|
||||
|
||||
class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
|
||||
{
|
||||
/**
|
||||
* @param AbstractInline|Link $inline
|
||||
* @param ElementRendererInterface $htmlRenderer
|
||||
*
|
||||
* @return HtmlElement
|
||||
*/
|
||||
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
|
||||
{
|
||||
if (!($inline instanceof Link)) {
|
||||
throw new \InvalidArgumentException('Incompatible inline type: ' . \get_class($inline));
|
||||
// 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
|
||||
@ -26,29 +33,19 @@ class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
|
||||
|
||||
// empty urls, anchors and absolute urls
|
||||
// should not go through the url resolver
|
||||
if (!DauxHelper::isValidUrl($url) || DauxHelper::isExternalUrl($url)) {
|
||||
if (!$this->isValidUrl($url) || $this->isExternalUrl($url)) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
// if there's a hash component in the url, ensure we
|
||||
// don't put that part through the resolver.
|
||||
$urlAndHash = explode('#', $url);
|
||||
$url = $urlAndHash[0];
|
||||
|
||||
//Internal links
|
||||
$file = DauxHelper::resolveInternalFile($this->daux, $url);
|
||||
$file = $this->resolveInternalFile($url);
|
||||
|
||||
$link_props = [];
|
||||
if (isset($urlAndHash[1])) {
|
||||
$link_props["ac:anchor"] = $urlAndHash[1];
|
||||
}
|
||||
|
||||
$page_props = [
|
||||
'ri:content-title' => trim(trim($this->daux['confluence']['prefix']) . ' ' . $file->getTitle()),
|
||||
$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', $page_props, '', true));
|
||||
$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>';
|
||||
@ -56,6 +53,6 @@ class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
|
||||
$children = '<ac:plain-text-link-body><![CDATA[' . $children . ']]></ac:plain-text-link-body>';
|
||||
}
|
||||
|
||||
return new HtmlElement('ac:link', $link_props, $page . $children);
|
||||
return new HtmlElement('ac:link', [], $page . $children);
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,11 @@
|
||||
use League\CommonMark\Block\Element\AbstractBlock;
|
||||
use League\CommonMark\Block\Renderer\BlockRendererInterface;
|
||||
use League\CommonMark\ElementRendererInterface;
|
||||
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
|
||||
|
||||
class TOCRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
|
||||
{
|
||||
if (!($block instanceof TableOfContents)) {
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
return '<ac:structured-macro ac:name="toc"></ac:structured-macro>';
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\Config as GlobalConfig;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\Console\RunAction;
|
||||
use Todaymade\Daux\Daux;
|
||||
use Todaymade\Daux\Tree\Content;
|
||||
@ -18,6 +18,9 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
/** @var Daux */
|
||||
protected $daux;
|
||||
|
||||
/**
|
||||
* @param Daux $daux
|
||||
*/
|
||||
public function __construct(Daux $daux)
|
||||
{
|
||||
$this->daux = $daux;
|
||||
@ -27,7 +30,7 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
|
||||
public function checkConfiguration()
|
||||
{
|
||||
$config = $this->daux->getConfig();
|
||||
$config = $this->daux->getParams();
|
||||
$confluence = $config->getConfluenceConfiguration();
|
||||
|
||||
if ($confluence == null) {
|
||||
@ -37,7 +40,7 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
$mandatory = ['space_id', 'base_url', 'user', 'pass', 'prefix'];
|
||||
$errors = [];
|
||||
foreach ($mandatory as $key) {
|
||||
if (!$confluence->hasValue($key)) {
|
||||
if (!array_key_exists($key, $confluence)) {
|
||||
$errors[] = $key;
|
||||
}
|
||||
}
|
||||
@ -46,7 +49,7 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
throw new \RuntimeException("The following options are mandatory for confluence : '" . implode("', '", $errors) . "'");
|
||||
}
|
||||
|
||||
if (!$confluence->hasAncestorId() && !$confluence->hasRootId()) {
|
||||
if (!array_key_exists('ancestor_id', $confluence) && !array_key_exists('root_id', $confluence)) {
|
||||
throw new \RuntimeException("You must specify an 'ancestor_id' or a 'root_id' for confluence.");
|
||||
}
|
||||
}
|
||||
@ -57,7 +60,7 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
public function getContentTypes()
|
||||
{
|
||||
return [
|
||||
new ContentTypes\Markdown\ContentType($this->daux->getConfig()),
|
||||
new ContentTypes\Markdown\ContentType($this->daux->getParams()),
|
||||
];
|
||||
}
|
||||
|
||||
@ -66,20 +69,18 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
*/
|
||||
public function generateAll(InputInterface $input, OutputInterface $output, $width)
|
||||
{
|
||||
$config = $this->daux->getConfig();
|
||||
$params = $this->daux->getParams();
|
||||
|
||||
$confluence = $config->getConfluenceConfiguration();
|
||||
$this->prefix = trim($confluence->getPrefix()) . ' ';
|
||||
if ($this->prefix == ' ') {
|
||||
$this->prefix = '';
|
||||
}
|
||||
$confluence = $params['confluence'];
|
||||
$this->prefix = trim($confluence['prefix']) . ' ';
|
||||
|
||||
$tree = $this->runAction(
|
||||
'Generating Tree ...',
|
||||
$output,
|
||||
$width,
|
||||
function () use ($config) {
|
||||
$tree = $this->generateRecursive($this->daux->tree, $config);
|
||||
$tree['title'] = $this->prefix . $config->getTitle();
|
||||
function () use ($params) {
|
||||
$tree = $this->generateRecursive($this->daux->tree, $params);
|
||||
$tree['title'] = $this->prefix . $params['title'];
|
||||
|
||||
return $tree;
|
||||
}
|
||||
@ -93,31 +94,31 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
|
||||
$publisher->publish($tree);
|
||||
}
|
||||
|
||||
private function generateRecursive(Directory $tree, GlobalConfig $config, $base_url = '')
|
||||
private function generateRecursive(Directory $tree, Config $params, $base_url = '')
|
||||
{
|
||||
$final = ['title' => $this->prefix . $tree->getTitle()];
|
||||
$config['base_url'] = $base_url;
|
||||
$params['base_url'] = $params['base_page'] = $base_url;
|
||||
|
||||
$config->setImage(str_replace('<base_url>', $base_url, $config->getImage()));
|
||||
$params['image'] = str_replace('<base_url>', $base_url, $params['image']);
|
||||
if ($base_url !== '') {
|
||||
$config->setEntryPage($tree->getFirstPage());
|
||||
$params['entry_page'] = $tree->getFirstPage();
|
||||
}
|
||||
foreach ($tree->getEntries() as $key => $node) {
|
||||
if ($node instanceof Directory) {
|
||||
$final['children'][$this->prefix . $node->getTitle()] = $this->generateRecursive(
|
||||
$node,
|
||||
$config,
|
||||
$params,
|
||||
'../' . $base_url
|
||||
);
|
||||
} elseif ($node instanceof Content) {
|
||||
$config->setRequest($node->getUrl());
|
||||
$params['request'] = $node->getUrl();
|
||||
|
||||
$contentType = $this->daux->getContentTypeHandler()->getType($node);
|
||||
|
||||
$data = [
|
||||
'title' => $this->prefix . $node->getTitle(),
|
||||
'file' => $node,
|
||||
'page' => ContentPage::fromFile($node, $config, $contentType),
|
||||
'page' => ContentPage::fromFile($node, $params, $contentType),
|
||||
];
|
||||
|
||||
if ($key == 'index.html') {
|
||||
|
@ -7,50 +7,71 @@ class Publisher
|
||||
{
|
||||
use RunAction;
|
||||
|
||||
/**
|
||||
* @var int terminal width
|
||||
*/
|
||||
public $width;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
public $output;
|
||||
|
||||
/**
|
||||
* @var Api
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
* @var array
|
||||
*/
|
||||
protected $confluence;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $previous_title;
|
||||
|
||||
/**
|
||||
* @var int terminal width
|
||||
*/
|
||||
public $width;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
public $output;
|
||||
|
||||
/**
|
||||
* @param $confluence
|
||||
*/
|
||||
public function __construct(Config $confluence)
|
||||
public function __construct($confluence)
|
||||
{
|
||||
$this->confluence = $confluence;
|
||||
|
||||
$this->client = new Api($confluence->getBaseUrl(), $confluence->getUser(), $confluence->getPassword());
|
||||
$this->client->setSpace($confluence->getSpaceId());
|
||||
$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->width, $closure);
|
||||
return $this->runAction($title, $this->output, $this->width, $closure);
|
||||
} catch (BadResponseException $e) {
|
||||
$this->output->writeLn('<fg=red>' . $e->getMessage() . '</>');
|
||||
$this->output->writeLn(' <error>' . $e->getMessage() . '</error>');
|
||||
}
|
||||
}
|
||||
|
||||
public function publish(array $tree)
|
||||
{
|
||||
$this->output->writeLn('Finding Root Page...');
|
||||
$published = $this->getRootPage($tree);
|
||||
echo "Finding Root Page...\n";
|
||||
if (array_key_exists('ancestor_id', $this->confluence)) {
|
||||
$pages = $this->client->getList($this->confluence['ancestor_id']);
|
||||
$published = null;
|
||||
foreach ($pages as $page) {
|
||||
if ($page['title'] == $tree['title']) {
|
||||
$published = $page;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif (array_key_exists('root_id', $this->confluence)) {
|
||||
$published = $this->client->getPage($this->confluence['root_id']);
|
||||
$this->confluence['ancestor_id'] = $published['ancestor_id'];
|
||||
} else {
|
||||
throw new \RuntimeException('You must at least specify a `root_id` or `ancestor_id` in your confluence configuration.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
$this->run(
|
||||
'Getting already published pages...',
|
||||
@ -62,43 +83,38 @@ class Publisher
|
||||
);
|
||||
|
||||
$published = $this->run(
|
||||
'Create placeholder pages...',
|
||||
"Create placeholder pages...\n",
|
||||
function () use ($tree, $published) {
|
||||
return $this->createRecursive($this->confluence->getAncestorId(), $tree, $published);
|
||||
return $this->createRecursive($this->confluence['ancestor_id'], $tree, $published);
|
||||
}
|
||||
);
|
||||
|
||||
$this->output->writeLn('Publishing updates...');
|
||||
$published = $this->updateRecursive($this->confluence->getAncestorId(), $tree, $published);
|
||||
$published = $this->updateRecursive($this->confluence['ancestor_id'], $tree, $published);
|
||||
|
||||
$delete = new PublisherDelete($this->output, $this->confluence->shouldAutoDeleteOrphanedPages(), $this->client);
|
||||
$delete->handle($published);
|
||||
|
||||
if ($this->shouldDelete()) {
|
||||
$this->output->writeLn('Deleting obsolete pages...');
|
||||
} else {
|
||||
$this->output->writeLn('Listing obsolete pages...');
|
||||
echo "> The following pages will not be deleted, but just listed for information.\n";
|
||||
echo "> If you want to delete these pages, you need to set the --delete flag on the command.\n";
|
||||
}
|
||||
$this->deleteRecursive($published);
|
||||
}
|
||||
|
||||
protected function getRootPage($tree)
|
||||
protected function niceTitle($title)
|
||||
{
|
||||
if ($this->confluence->hasAncestorId()) {
|
||||
$pages = $this->client->getList($this->confluence->getAncestorId());
|
||||
foreach ($pages as $page) {
|
||||
if ($page['title'] == $tree['title']) {
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
if ($title == 'index.html') {
|
||||
return 'Homepage';
|
||||
}
|
||||
|
||||
if ($this->confluence->hasRootId()) {
|
||||
$published = $this->client->getPage($this->confluence->getRootId());
|
||||
$this->confluence->setAncestorId($published['ancestor_id']);
|
||||
|
||||
return $published;
|
||||
}
|
||||
|
||||
throw new \RuntimeException('You must at least specify a `root_id` or `ancestor_id` in your confluence configuration.');
|
||||
return rtrim(strtr($title, ['index.html' => '', '.html' => '']), '/');
|
||||
}
|
||||
|
||||
protected function createPage($parent_id, $entry, $published)
|
||||
{
|
||||
echo '- ' . PublisherUtilities::niceTitle($entry['file']->getUrl()) . "\n";
|
||||
echo '- ' . $this->niceTitle($entry['file']->getUrl()) . "\n";
|
||||
$published['version'] = 1;
|
||||
$published['title'] = $entry['title'];
|
||||
$published['id'] = $this->client->createPage($parent_id, $entry['title'], 'The content will come very soon !');
|
||||
@ -176,15 +192,88 @@ class Publisher
|
||||
return $this->recursiveWithCallback($parent_id, $entry, $published, $callback);
|
||||
}
|
||||
|
||||
protected function shouldDelete()
|
||||
{
|
||||
return array_key_exists('delete', $this->confluence) && $this->confluence['delete'];
|
||||
}
|
||||
|
||||
protected function deleteRecursive($published, $prefix = '')
|
||||
{
|
||||
foreach ($published['children'] as $child) {
|
||||
if (array_key_exists('children', $child) && count($child['children'])) {
|
||||
$this->deleteRecursive($child, $child['title'] . '/');
|
||||
}
|
||||
|
||||
if (!array_key_exists('needed', $child)) {
|
||||
if ($this->shouldDelete()) {
|
||||
$this->run(
|
||||
'- ' . $prefix . $child['title'],
|
||||
function () use ($child) {
|
||||
$this->client->deletePage($child['id']);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
echo '- ' . $prefix . $child['title'] . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function shouldUpdate($local, $local_content, $published)
|
||||
{
|
||||
if (!array_key_exists('content', $published)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$trimmed_local = trim($local_content);
|
||||
$trimmed_distant = trim($published['content']);
|
||||
|
||||
if ($trimmed_local == $trimmed_distant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
// This is configurable with `update_threshold`
|
||||
$threshold = 98;
|
||||
if (array_key_exists('update_threshold', $this->confluence)) {
|
||||
$threshold = 100 - $this->confluence['update_threshold'];
|
||||
}
|
||||
|
||||
if ($threshold < 100) {
|
||||
similar_text($trimmed_local, $trimmed_distant, $percent);
|
||||
if ($percent > $threshold) {
|
||||
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)
|
||||
{
|
||||
$updateThreshold = $this->confluence->getUpdateThreshold();
|
||||
if ($this->previous_title != 'Updating') {
|
||||
$this->previous_title = 'Updating';
|
||||
echo "Updating Pages...\n";
|
||||
}
|
||||
|
||||
$this->run(
|
||||
'- ' . PublisherUtilities::niceTitle($entry['file']->getUrl()),
|
||||
function () use ($entry, $published, $parent_id, $updateThreshold) {
|
||||
'- ' . $this->niceTitle($entry['file']->getUrl()),
|
||||
function () use ($entry, $published, $parent_id) {
|
||||
$generated_content = $entry['page']->getContent();
|
||||
if (PublisherUtilities::shouldUpdate($entry['page'], $generated_content, $published, $updateThreshold)) {
|
||||
if ($this->shouldUpdate($entry['page'], $generated_content, $published)) {
|
||||
$this->client->updatePage(
|
||||
$parent_id,
|
||||
$published['id'],
|
||||
@ -200,8 +289,8 @@ class Publisher
|
||||
foreach ($entry['page']->attachments as $attachment) {
|
||||
$this->run(
|
||||
" With attachment: $attachment[filename]",
|
||||
function ($write) use ($published, $attachment) {
|
||||
$this->client->uploadAttachment($published['id'], $attachment, $write);
|
||||
function () use ($published, $attachment) {
|
||||
$this->client->uploadAttachment($published['id'], $attachment);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\Confluence;
|
||||
|
||||
class PublisherDelete
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
public $output;
|
||||
|
||||
/**
|
||||
* @var array files that can be deleted
|
||||
*/
|
||||
protected $deletable;
|
||||
|
||||
/**
|
||||
* @var bool should delete ?
|
||||
*/
|
||||
protected $delete;
|
||||
|
||||
/**
|
||||
* @var Api
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
public function __construct($output, bool $delete, $client)
|
||||
{
|
||||
$this->output = $output;
|
||||
$this->delete = $delete;
|
||||
$this->client = $client;
|
||||
|
||||
$this->deletable = [];
|
||||
}
|
||||
|
||||
protected function listDeletable($published, $prefix = '')
|
||||
{
|
||||
foreach ($published['children'] as $child) {
|
||||
if (array_key_exists('children', $child) && count($child['children'])) {
|
||||
$this->listDeletable($child, $child['title'] . '/');
|
||||
}
|
||||
|
||||
if (!array_key_exists('needed', $child)) {
|
||||
$this->deletable[$child['id']] = $prefix . $child['title'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function handle($published)
|
||||
{
|
||||
$this->listDeletable($published);
|
||||
|
||||
if (!count($this->deletable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->delete) {
|
||||
$this->doDelete();
|
||||
} else {
|
||||
$this->displayDeletable();
|
||||
}
|
||||
}
|
||||
|
||||
protected function doDelete()
|
||||
{
|
||||
$this->output->writeLn('Deleting obsolete pages...');
|
||||
foreach ($this->deletable as $id => $title) {
|
||||
$this->output->writeLn("- $title");
|
||||
$this->client->deletePage($id);
|
||||
}
|
||||
}
|
||||
|
||||
protected function displayDeletable()
|
||||
{
|
||||
$this->output->writeLn('Listing obsolete pages...');
|
||||
$this->output->writeLn('> The following pages will not be deleted, but just listed for information.');
|
||||
$this->output->writeLn('> If you want to delete these pages, you need to set the --delete flag on the command.');
|
||||
foreach ($this->deletable as $id => $title) {
|
||||
$this->output->writeLn("- $title");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\Confluence;
|
||||
|
||||
class PublisherUtilities
|
||||
{
|
||||
public static function niceTitle($title)
|
||||
{
|
||||
if ($title == 'index.html') {
|
||||
return 'Homepage';
|
||||
}
|
||||
|
||||
return rtrim(strtr($title, ['index.html' => '', '.html' => '']), '/');
|
||||
}
|
||||
|
||||
public static function shouldUpdate($local, $local_content, $published, $threshold)
|
||||
{
|
||||
if (!array_key_exists('content', $published)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$trimmed_local = trim($local_content);
|
||||
$trimmed_distant = trim($published['content']);
|
||||
|
||||
if ($trimmed_local == $trimmed_distant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
// This is configurable with `update_threshold`
|
||||
$threshold = 100 - $threshold;
|
||||
|
||||
if ($threshold < 100) {
|
||||
similar_text($trimmed_local, $trimmed_distant, $percent);
|
||||
if ($percent > $threshold) {
|
||||
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;
|
||||
}
|
||||
}
|
@ -10,182 +10,27 @@ class Config extends BaseConfig
|
||||
|
||||
return [
|
||||
'name' => 'GitHub',
|
||||
'basepath' => (strpos($url, 'https://github.com/') === 0 ? '' : 'https://github.com/') . trim($url, '/'),
|
||||
'basepath' => (strpos($url, 'https://github.com/') === 0 ? '' : 'https://github.com/') . trim($url, '/')
|
||||
];
|
||||
}
|
||||
|
||||
public function getEditOn()
|
||||
function getEditOn()
|
||||
{
|
||||
if ($this->hasValue('edit_on')) {
|
||||
$edit_on = $this->getValue('edit_on');
|
||||
if (is_string($edit_on)) {
|
||||
return $this->prepareGithubUrl($edit_on);
|
||||
}
|
||||
$edit_on['basepath'] = rtrim($edit_on['basepath'], '/');
|
||||
if (array_key_exists('edit_on', $this)) {
|
||||
if (is_string($this['edit_on'])) {
|
||||
return $this->prepareGithubUrl($this['edit_on']);
|
||||
} else {
|
||||
|
||||
return $edit_on;
|
||||
$this['edit_on']['basepath'] = rtrim($this['edit_on']['basepath'], '/');
|
||||
|
||||
return $this['edit_on'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasValue('edit_on_github')) {
|
||||
return $this->prepareGithubUrl($this->getValue('edit_on_github'));
|
||||
if (array_key_exists('edit_on_github', $this)) {
|
||||
return $this->prepareGithubUrl($this['edit_on_github']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hasSearch()
|
||||
{
|
||||
return $this->hasValue('search') && $this->getValue('search');
|
||||
}
|
||||
|
||||
public function showDateModified()
|
||||
{
|
||||
return $this->hasValue('date_modified') && $this->getValue('date_modified');
|
||||
}
|
||||
|
||||
public function showPreviousNextLinks()
|
||||
{
|
||||
if ($this->hasValue('jump_buttons')) {
|
||||
return $this->getValue('jump_buttons');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function showCodeToggle()
|
||||
{
|
||||
if ($this->hasValue('toggle_code')) {
|
||||
return $this->getValue('toggle_code');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasAutomaticTableOfContents(): bool
|
||||
{
|
||||
return $this->hasValue('auto_toc') && $this->getValue('auto_toc');
|
||||
}
|
||||
|
||||
public function hasGoogleAnalytics()
|
||||
{
|
||||
return $this->hasValue('google_analytics') && $this->getValue('google_analytics');
|
||||
}
|
||||
|
||||
public function getGoogleAnalyticsId()
|
||||
{
|
||||
return $this->getValue('google_analytics');
|
||||
}
|
||||
|
||||
public function hasPlausibleAnalyticsDomain()
|
||||
{
|
||||
return $this->hasValue('plausible_domain') && $this->getValue('plausible_domain');
|
||||
}
|
||||
|
||||
public function getPlausibleAnalyticsDomain()
|
||||
{
|
||||
return $this->getValue('plausible_domain');
|
||||
}
|
||||
|
||||
public function hasPiwikAnalytics()
|
||||
{
|
||||
return $this->getValue('piwik_analytics') && $this->hasValue('piwik_analytics_id');
|
||||
}
|
||||
|
||||
public function getPiwikAnalyticsId()
|
||||
{
|
||||
return $this->getValue('piwik_analytics_id');
|
||||
}
|
||||
|
||||
public function getPiwikAnalyticsUrl()
|
||||
{
|
||||
return $this->getValue('piwik_analytics');
|
||||
}
|
||||
|
||||
public function hasPoweredBy()
|
||||
{
|
||||
return $this->hasValue('powered_by') && !empty($this->getValue('powered_by'));
|
||||
}
|
||||
|
||||
public function getPoweredBy()
|
||||
{
|
||||
return $this->getValue('powered_by');
|
||||
}
|
||||
|
||||
public function hasTwitterHandles()
|
||||
{
|
||||
return $this->hasValue('twitter') && !empty($this->getValue('twitter'));
|
||||
}
|
||||
|
||||
public function getTwitterHandles()
|
||||
{
|
||||
return $this->getValue('twitter');
|
||||
}
|
||||
|
||||
public function hasLinks()
|
||||
{
|
||||
return $this->hasValue('links') && !empty($this->getValue('links'));
|
||||
}
|
||||
|
||||
public function getLinks()
|
||||
{
|
||||
return $this->getValue('links');
|
||||
}
|
||||
|
||||
public function hasRepository()
|
||||
{
|
||||
return $this->hasValue('repo') && !empty($this->getValue('repo'));
|
||||
}
|
||||
|
||||
public function getRepository()
|
||||
{
|
||||
return $this->getValue('repo');
|
||||
}
|
||||
|
||||
public function hasButtons()
|
||||
{
|
||||
return $this->hasValue('buttons') && !empty($this->getValue('buttons'));
|
||||
}
|
||||
|
||||
public function getButtons()
|
||||
{
|
||||
return $this->getValue('buttons');
|
||||
}
|
||||
|
||||
public function hasLandingPage()
|
||||
{
|
||||
if ($this->hasValue('auto_landing')) {
|
||||
return $this->getValue('auto_landing');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasBreadcrumbs()
|
||||
{
|
||||
if ($this->hasValue('breadcrumbs')) {
|
||||
return $this->getValue('breadcrumbs');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBreadcrumbsSeparator()
|
||||
{
|
||||
return $this->getValue('breadcrumb_separator');
|
||||
}
|
||||
|
||||
public function getTheme()
|
||||
{
|
||||
return $this->getValue('theme');
|
||||
}
|
||||
|
||||
public function hasThemeVariant()
|
||||
{
|
||||
return $this->hasValue('theme-variant') && !empty($this->getValue('theme-variant'));
|
||||
}
|
||||
|
||||
public function getThemeVariant()
|
||||
{
|
||||
return $this->getValue('theme-variant');
|
||||
}
|
||||
}
|
||||
|
@ -4,34 +4,27 @@ use Todaymade\Daux\Tree\Root;
|
||||
|
||||
class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
{
|
||||
/**
|
||||
* @var Template
|
||||
*/
|
||||
public $templateRenderer;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $language;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $homepage;
|
||||
|
||||
private function isHomepage(): bool
|
||||
private function isHomepage()
|
||||
{
|
||||
// If the current page isn't the index, no chance it is the landing page
|
||||
if ($this->file->getParent()->getIndexPage() != $this->file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the direct parent is root, this is the homepage
|
||||
// If the direct parent is root, this is the homage
|
||||
return $this->file->getParent() instanceof Root;
|
||||
}
|
||||
|
||||
private function isLanding(): bool
|
||||
{
|
||||
return $this->config->getHTML()->hasLandingPage() && $this->homepage;
|
||||
private function isLanding() {
|
||||
// If we don't have the auto_landing parameter, we don't want any homepage
|
||||
if (array_key_exists('auto_landing', $this->params['html']) && !$this->params['html']['auto_landing']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->homepage;
|
||||
}
|
||||
|
||||
private function initialize()
|
||||
@ -39,7 +32,7 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
$this->homepage = $this->isHomepage();
|
||||
|
||||
$this->language = '';
|
||||
if ($this->config->isMultilanguage() && count($this->file->getParents())) {
|
||||
if ($this->params->isMultilanguage() && count($this->file->getParents())) {
|
||||
$language_dir = $this->file->getParents()[0];
|
||||
$this->language = $language_dir->getName();
|
||||
}
|
||||
@ -48,7 +41,6 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
/**
|
||||
* @param \Todaymade\Daux\Tree\Directory[] $parents
|
||||
* @param bool $multilanguage
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBreadcrumbTrail($parents, $multilanguage)
|
||||
@ -60,7 +52,7 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
if (!empty($parents)) {
|
||||
foreach ($parents as $node) {
|
||||
$page = $node->getIndexPage() ?: $node->getFirstPage();
|
||||
$breadcrumb_trail[] = ['title' => $node->getTitle(), 'url' => $page ? $page->getUrl() : ''];
|
||||
$breadcrumb_trail[$node->getTitle()] = $page ? $page->getUrl() : '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,16 +62,16 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
protected function generatePage()
|
||||
{
|
||||
$this->initialize();
|
||||
$config = $this->config;
|
||||
$params = $this->params;
|
||||
|
||||
$entry_page = [];
|
||||
if ($this->homepage) {
|
||||
if ($config->isMultilanguage()) {
|
||||
foreach ($config->getLanguages() as $key => $name) {
|
||||
$entry_page[$name] = $config->getBasePage() . $config->getEntryPage()[$key]->getUrl();
|
||||
if ($params->isMultilanguage()) {
|
||||
foreach ($params['languages'] as $key => $name) {
|
||||
$entry_page[$name] = $params['base_page'] . $params['entry_page'][$key]->getUrl();
|
||||
}
|
||||
} else {
|
||||
$entry_page['__VIEW_DOCUMENTATION__'] = $config->getBasePage() . $config->getEntryPage()->getUrl();
|
||||
$entry_page['View Documentation'] = $params['base_page'] . $params['entry_page']->getUrl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,34 +85,26 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
'relative_path' => $this->file->getRelativePath(),
|
||||
'modified_time' => filemtime($this->file->getPath()),
|
||||
'markdown' => $this->content,
|
||||
'request' => $config->getRequest(),
|
||||
'request' => $params['request'],
|
||||
'content' => $this->getPureContent(),
|
||||
'breadcrumbs' => $config->getHTML()->hasBreadcrumbs(),
|
||||
'breadcrumbs' => $params['html']['breadcrumbs'],
|
||||
'prev' => $this->file->getPrevious(),
|
||||
'next' => $this->file->getNext(),
|
||||
'attributes' => $this->file->getAttribute(),
|
||||
];
|
||||
|
||||
if ($page['breadcrumbs']) {
|
||||
$page['breadcrumb_trail'] = $this->getBreadcrumbTrail($this->file->getParents(), $config->isMultilanguage());
|
||||
$page['breadcrumb_separator'] = $this->config->getHTML()->getBreadcrumbsSeparator();
|
||||
$page['breadcrumb_trail'] = $this->getBreadcrumbTrail($this->file->getParents(), $params->isMultilanguage());
|
||||
$page['breadcrumb_separator'] = $params['html']['breadcrumb_separator'];
|
||||
|
||||
if ($this->homepage) {
|
||||
$page['breadcrumb_trail'] = [['title' => $this->file->getTitle(), 'url' => '']];
|
||||
$page['breadcrumb_trail'] = [$this->file->getTitle() => ''];
|
||||
}
|
||||
}
|
||||
|
||||
$context = ['page' => $page, 'config' => $config];
|
||||
$context = ['page' => $page, 'params' => $params];
|
||||
|
||||
$template = 'theme::content';
|
||||
if ($this->isLanding()) {
|
||||
$template = 'theme::home';
|
||||
}
|
||||
$template = new Template($params['templates'], $params['theme']['templates']);
|
||||
|
||||
if (array_key_exists('template', $page['attributes'])) {
|
||||
$template = 'theme::' . $page['attributes']['template'];
|
||||
}
|
||||
|
||||
return $this->templateRenderer->render($template, $context);
|
||||
return $template->render($this->isLanding() ? 'theme::home' : 'theme::content', $context);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\Block\Element as BlockElement;
|
||||
use League\CommonMark\Environment;
|
||||
use League\CommonMark\Event\DocumentParsedEvent;
|
||||
use League\CommonMark\Inline\Element as InlineElement;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
|
||||
|
||||
class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter
|
||||
{
|
||||
@ -13,12 +9,7 @@ class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMa
|
||||
{
|
||||
parent::extendEnvironment($environment, $config);
|
||||
|
||||
$environment->addBlockRenderer(BlockElement\FencedCode::class, new FencedCodeRenderer());
|
||||
|
||||
$processor = new TOC\Processor($config);
|
||||
$environment->addEventListener(DocumentParsedEvent::class, [$processor, 'onDocumentParsed']);
|
||||
$environment->addBlockRenderer(TableOfContents::class, new TOC\Renderer($config));
|
||||
|
||||
$environment->addInlineRenderer(InlineElement\Image::class, new ImageRenderer($config));
|
||||
$environment->addDocumentProcessor(new TOC\Processor($config));
|
||||
$environment->addBlockRenderer('Todaymade\Daux\ContentTypes\Markdown\TableOfContents', new TOC\Renderer());
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
|
||||
|
||||
use Todaymade\Daux\Config;
|
||||
|
||||
class ContentType extends \Todaymade\Daux\ContentTypes\Markdown\ContentType
|
||||
{
|
||||
protected function createConverter()
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
return new CommonMarkConverter(['daux' => $this->config]);
|
||||
$this->config = $config;
|
||||
$this->converter = new CommonMarkConverter(['daux' => $config]);
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
|
||||
|
||||
use Highlight\Highlighter;
|
||||
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;
|
||||
use League\CommonMark\Util\Xml;
|
||||
|
||||
class FencedCodeRenderer implements BlockRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var Highlighter
|
||||
*/
|
||||
private $hl;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->hl = new Highlighter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
}
|
||||
|
||||
$attrs = [];
|
||||
foreach ($block->getData('attributes', []) as $key => $value) {
|
||||
$attrs[$key] = Xml::escape($value);
|
||||
}
|
||||
|
||||
$content = $block->getStringContent();
|
||||
|
||||
$language = $this->getLanguage($block->getInfoWords());
|
||||
$highlighted = false;
|
||||
if ($language) {
|
||||
$attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' ' : '';
|
||||
|
||||
try {
|
||||
$highlighted = $this->hl->highlight($language, $content);
|
||||
$content = $highlighted->value;
|
||||
$attrs['class'] .= 'hljs ' . $highlighted->language;
|
||||
} catch (\Exception $e) {
|
||||
$attrs['class'] .= 'language-' . $language;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$highlighted) {
|
||||
$content = Xml::escape($content);
|
||||
}
|
||||
|
||||
return new HtmlElement(
|
||||
'pre',
|
||||
[],
|
||||
new HtmlElement('code', $attrs, $content)
|
||||
);
|
||||
}
|
||||
|
||||
public function getLanguage($infoWords)
|
||||
{
|
||||
if (count($infoWords) === 0 || strlen($infoWords[0]) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Xml::escape($infoWords[0]);
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\ElementRendererInterface;
|
||||
use League\CommonMark\HtmlElement;
|
||||
use League\CommonMark\Inline\Element\AbstractInline;
|
||||
use League\CommonMark\Inline\Element\Image;
|
||||
use League\CommonMark\Inline\Renderer\InlineRendererInterface;
|
||||
use League\CommonMark\Util\ConfigurationAwareInterface;
|
||||
use League\CommonMark\Util\ConfigurationInterface;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\DauxHelper;
|
||||
use Todaymade\Daux\Exception\LinkNotFoundException;
|
||||
|
||||
class ImageRenderer implements InlineRendererInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $daux;
|
||||
|
||||
/**
|
||||
* @var ConfigurationInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \League\CommonMark\Inline\Renderer\ImageRenderer
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
public function __construct($daux)
|
||||
{
|
||||
$this->daux = $daux;
|
||||
$this->parent = new \League\CommonMark\Inline\Renderer\ImageRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relative URLs can be done using either the folder with
|
||||
* number prefix or the final name (with prefix stripped).
|
||||
* This ensures that we always use the final name when generating.
|
||||
*
|
||||
* @param mixed $url
|
||||
*
|
||||
* @throws LinkNotFoundException
|
||||
*/
|
||||
protected function getCleanUrl($url)
|
||||
{
|
||||
// empty urls and anchors should
|
||||
// not go through the url resolver
|
||||
if (!DauxHelper::isValidUrl($url)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
// Absolute urls, shouldn't either
|
||||
if (DauxHelper::isExternalUrl($url)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
try {
|
||||
$file = DauxHelper::resolveInternalFile($this->daux, $url);
|
||||
|
||||
return DauxHelper::getRelativePath($this->daux->getCurrentPage()->getUrl(), $file->getUrl());
|
||||
} catch (LinkNotFoundException $e) {
|
||||
if ($this->daux->isStatic()) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Image $inline
|
||||
*
|
||||
* @throws LinkNotFoundException
|
||||
*
|
||||
* @return HtmlElement
|
||||
*/
|
||||
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
|
||||
{
|
||||
if (!($inline instanceof Image)) {
|
||||
throw new \InvalidArgumentException('Incompatible inline type: ' . \get_class($inline));
|
||||
}
|
||||
|
||||
$inline->setUrl($this->getCleanUrl($inline->getUrl()));
|
||||
|
||||
return $this->parent->render($inline, $htmlRenderer);
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration)
|
||||
{
|
||||
$this->config = $configuration;
|
||||
$this->parent->setConfiguration($configuration);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ class Entry
|
||||
{
|
||||
protected $content;
|
||||
protected $level;
|
||||
protected $parent;
|
||||
protected $parent = null;
|
||||
protected $children = [];
|
||||
|
||||
public function __construct(Heading $content)
|
||||
@ -56,6 +56,7 @@ class Entry
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entry $parent
|
||||
* @param bool $addChild
|
||||
*/
|
||||
public function setParent(Entry $parent, $addChild = true)
|
||||
@ -66,6 +67,9 @@ class Entry
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entry $child
|
||||
*/
|
||||
public function addChild(Entry $child)
|
||||
{
|
||||
$child->setParent($this, false);
|
||||
|
@ -7,20 +7,16 @@ use League\CommonMark\Block\Element\ListBlock;
|
||||
use League\CommonMark\Block\Element\ListData;
|
||||
use League\CommonMark\Block\Element\ListItem;
|
||||
use League\CommonMark\Block\Element\Paragraph;
|
||||
use League\CommonMark\Event\DocumentParsedEvent;
|
||||
use League\CommonMark\DocumentProcessorInterface;
|
||||
use League\CommonMark\Inline\Element\Link;
|
||||
use League\CommonMark\Inline\Element\Text;
|
||||
use League\CommonMark\Node\Node;
|
||||
use ReflectionMethod;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
|
||||
use Todaymade\Daux\DauxHelper;
|
||||
|
||||
class Processor
|
||||
class Processor implements DocumentProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
public function __construct(Config $config)
|
||||
@ -28,9 +24,18 @@ class Processor
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function onDocumentParsed(DocumentParsedEvent $event)
|
||||
public function hasAutoTOC()
|
||||
{
|
||||
return array_key_exists('html', $this->config) && array_key_exists('auto_toc', $this->config['html']) && $this->config['html']['auto_toc'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Document $document
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function processDocument(Document $document)
|
||||
{
|
||||
$document = $event->getDocument();
|
||||
/** @var TableOfContents[] $tocs */
|
||||
$tocs = [];
|
||||
|
||||
@ -43,7 +48,6 @@ class Processor
|
||||
|
||||
if ($node instanceof TableOfContents && !$event->isEntering()) {
|
||||
$tocs[] = $node;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -55,7 +59,7 @@ class Processor
|
||||
$headings[] = new Entry($node);
|
||||
}
|
||||
|
||||
if (count($headings) && (count($tocs) || $this->config->getHTML()->hasAutomaticTableOfContents())) {
|
||||
if (count($headings) && (count($tocs) || $this->hasAutoTOC())) {
|
||||
$generated = $this->generate($headings);
|
||||
|
||||
if (count($tocs)) {
|
||||
@ -68,27 +72,43 @@ class Processor
|
||||
}
|
||||
}
|
||||
|
||||
protected function getUniqueId(Document $document, $proposed)
|
||||
{
|
||||
if ($proposed == 'page_') {
|
||||
$proposed = 'page_section_' . (count($document->heading_ids) + 1);
|
||||
/**
|
||||
* Get an escaped version of the link
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected function escaped($url) {
|
||||
$url = trim($url);
|
||||
$url = preg_replace('~[^\\pL0-9_]+~u', '-', $url);
|
||||
$url = trim($url, "-");
|
||||
$url = iconv("utf-8", "us-ascii//TRANSLIT", $url);
|
||||
$url = preg_replace('~[^-a-zA-Z0-9_]+~', '', $url);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function getUniqueId(Document $document, $proposed) {
|
||||
if ($proposed == "page_") {
|
||||
$proposed = "page_section_" . (count($document->heading_ids) + 1);
|
||||
}
|
||||
|
||||
// Quick path, it's a unique ID
|
||||
if (!in_array($proposed, $document->heading_ids)) {
|
||||
$document->heading_ids[] = $proposed;
|
||||
|
||||
return $proposed;
|
||||
}
|
||||
|
||||
$extension = 1; // Initialize the variable at one, so on the first iteration we have 2
|
||||
do {
|
||||
++$extension;
|
||||
$extension++;
|
||||
} while (in_array("$proposed-$extension", $document->heading_ids));
|
||||
|
||||
return "$proposed-$extension";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Heading $node
|
||||
*/
|
||||
protected function ensureHeadingHasId(Document $document, Heading $node)
|
||||
{
|
||||
// If the node has an ID, no need to generate it, just check it's unique
|
||||
@ -119,14 +139,13 @@ class Processor
|
||||
}
|
||||
}
|
||||
|
||||
$node->data['attributes']['id'] = $this->getUniqueId($document, 'page_' . DauxHelper::slug($text));
|
||||
$node->data['attributes']['id'] = $this->getUniqueId($document,'page_'. $this->escaped($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a tree of the list of headings.
|
||||
* Make a tree of the list of headings
|
||||
*
|
||||
* @param Entry[] $headings
|
||||
*
|
||||
* @return RootEntry
|
||||
*/
|
||||
public function generate($headings)
|
||||
@ -142,21 +161,19 @@ class Processor
|
||||
|
||||
$parent->addChild($heading);
|
||||
$previous = $heading;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ($heading->getLevel() > $previous->getLevel()) {
|
||||
$previous->addChild($heading);
|
||||
$previous = $heading;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//if ($heading->getLevel() == $previous->getLevel()) {
|
||||
$previous->getParent()->addChild($heading);
|
||||
$previous = $heading;
|
||||
|
||||
continue;
|
||||
//}
|
||||
}
|
||||
@ -166,7 +183,6 @@ class Processor
|
||||
|
||||
/**
|
||||
* @param Entry[] $entries
|
||||
*
|
||||
* @return ListBlock
|
||||
*/
|
||||
protected function render(array $entries)
|
||||
@ -218,6 +234,7 @@ class Processor
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Heading $node
|
||||
* @return Node[]
|
||||
*/
|
||||
protected function cloneChildren(Heading $node)
|
||||
@ -237,6 +254,8 @@ class Processor
|
||||
$method->invoke($subnode, $firstClone);
|
||||
}
|
||||
|
||||
return (new DeepCopy())->copy($firstClone)->children();
|
||||
$deepCopy = new DeepCopy();
|
||||
|
||||
return $deepCopy->copy($firstClone)->children();
|
||||
}
|
||||
}
|
||||
|
@ -3,31 +3,11 @@
|
||||
use League\CommonMark\Block\Element\AbstractBlock;
|
||||
use League\CommonMark\Block\Renderer\BlockRendererInterface;
|
||||
use League\CommonMark\ElementRendererInterface;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
|
||||
|
||||
class Renderer implements BlockRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
|
||||
{
|
||||
if (!($block instanceof TableOfContents)) {
|
||||
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
|
||||
}
|
||||
|
||||
$content = $htmlRenderer->renderBlocks($block->children());
|
||||
|
||||
return $this->config->templateRenderer
|
||||
->getEngine($this->config)
|
||||
->render('theme::partials/table_of_contents', ['content' => $content]);
|
||||
return $htmlRenderer->renderBlocks($block->children());
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\Config as GlobalConfig;
|
||||
use Todaymade\Daux\Config;
|
||||
use Todaymade\Daux\Console\RunAction;
|
||||
use Todaymade\Daux\Daux;
|
||||
use Todaymade\Daux\DauxHelper;
|
||||
@ -17,23 +17,18 @@ use Todaymade\Daux\Tree\Raw;
|
||||
class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
{
|
||||
use RunAction;
|
||||
use HTMLUtils;
|
||||
|
||||
/** @var Daux */
|
||||
protected $daux;
|
||||
|
||||
/** @var Template */
|
||||
protected $templateRenderer;
|
||||
|
||||
protected $indexed_pages = [];
|
||||
|
||||
/**
|
||||
* @param Daux $daux
|
||||
*/
|
||||
public function __construct(Daux $daux)
|
||||
{
|
||||
$config = $daux->getConfig();
|
||||
|
||||
$this->daux = $daux;
|
||||
$this->templateRenderer = new Template($config);
|
||||
$config->templateRenderer = $this->templateRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,47 +37,71 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
public function getContentTypes()
|
||||
{
|
||||
return [
|
||||
'markdown' => new ContentType($this->daux->getConfig()),
|
||||
'markdown' => new ContentType($this->daux->getParams()),
|
||||
];
|
||||
}
|
||||
|
||||
protected function ensureEmptyDestination($destination)
|
||||
{
|
||||
if (is_dir($destination)) {
|
||||
GeneratorHelper::rmdir($destination);
|
||||
} else {
|
||||
mkdir($destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all files from $local to $destination
|
||||
*
|
||||
* @param string $destination
|
||||
* @param string $local_base
|
||||
*/
|
||||
protected function copyThemes($destination, $local_base)
|
||||
{
|
||||
mkdir($destination . DIRECTORY_SEPARATOR . 'themes');
|
||||
GeneratorHelper::copyRecursive(
|
||||
$local_base,
|
||||
$destination . DIRECTORY_SEPARATOR . 'themes'
|
||||
);
|
||||
}
|
||||
|
||||
public function generateAll(InputInterface $input, OutputInterface $output, $width)
|
||||
{
|
||||
$destination = $input->getOption('destination');
|
||||
|
||||
$config = $this->daux->getConfig();
|
||||
$params = $this->daux->getParams();
|
||||
if (is_null($destination)) {
|
||||
$destination = $config->getLocalBase() . DIRECTORY_SEPARATOR . 'static';
|
||||
$destination = $this->daux->local_base . DIRECTORY_SEPARATOR . 'static';
|
||||
}
|
||||
|
||||
$this->runAction(
|
||||
'Copying Static assets ...',
|
||||
$output,
|
||||
$width,
|
||||
function () use ($destination, $config) {
|
||||
function () use ($destination, $params) {
|
||||
$this->ensureEmptyDestination($destination);
|
||||
|
||||
$this->copyThemes($destination, $config->getThemesPath());
|
||||
$this->copyThemes($destination, $params->getThemesPath());
|
||||
}
|
||||
);
|
||||
|
||||
$output->writeLn('Generating ...');
|
||||
|
||||
$this->generateRecursive($this->daux->tree, $destination, $config, $output, $width, $config->getHTML()->hasSearch());
|
||||
|
||||
GeneratorHelper::copyRecursive(
|
||||
$config->getLocalBase() . DIRECTORY_SEPARATOR . 'daux_libraries' . DIRECTORY_SEPARATOR,
|
||||
$destination . DIRECTORY_SEPARATOR . 'daux_libraries'
|
||||
);
|
||||
|
||||
if ($config->getHTML()->hasSearch()) {
|
||||
file_put_contents(
|
||||
$destination . DIRECTORY_SEPARATOR . 'daux_search_index.js',
|
||||
'load_search_index(' . json_encode(['pages' => $this->indexed_pages]) . ');'
|
||||
);
|
||||
|
||||
if (json_last_error()) {
|
||||
echo "Could not write search index: \n" . json_last_error_msg() . "\n";
|
||||
if (!array_key_exists('search', $params['html']) || !$params['html']['search']) {
|
||||
$params['html']['search'] = $input->getOption('search');
|
||||
}
|
||||
|
||||
$this->generateRecursive($this->daux->tree, $destination, $params, $output, $width, $params['html']['search']);
|
||||
|
||||
if ($params['html']['search']) {
|
||||
GeneratorHelper::copyRecursive(
|
||||
$this->daux->local_base . DIRECTORY_SEPARATOR . 'tipuesearch' . DIRECTORY_SEPARATOR,
|
||||
$destination . DIRECTORY_SEPARATOR . 'tipuesearch'
|
||||
);
|
||||
file_put_contents(
|
||||
$destination . DIRECTORY_SEPARATOR . 'tipuesearch' . DIRECTORY_SEPARATOR . 'tipuesearch_content.json',
|
||||
json_encode(['pages' => $this->indexed_pages])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,13 +110,12 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
* script code, and embedded objects. Add line breaks around
|
||||
* block-level tags to prevent word joining after tag removal.
|
||||
* Also collapse whitespace to single space and trim result.
|
||||
* modified from: http://nadeausoftware.com/articles/2007/09/php_tip_how_strip_html_tags_web_page.
|
||||
* modified from: http://nadeausoftware.com/articles/2007/09/php_tip_how_strip_html_tags_web_page
|
||||
*
|
||||
* @param string $text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize($text)
|
||||
private function strip_html_tags($text)
|
||||
{
|
||||
$text = preg_replace(
|
||||
[
|
||||
@ -128,45 +146,43 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
$text
|
||||
);
|
||||
|
||||
$text = trim(preg_replace('/\s+/', ' ', strip_tags($text)));
|
||||
|
||||
// Sometimes strings are detected as invalid UTF-8 and json_encode can't treat them
|
||||
// iconv can fix those strings
|
||||
return iconv('UTF-8', 'UTF-8//IGNORE', $text);
|
||||
return trim(preg_replace('/\s+/', ' ', strip_tags($text)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively generate the documentation.
|
||||
* Recursively generate the documentation
|
||||
*
|
||||
* @param Directory $tree
|
||||
* @param string $output_dir
|
||||
* @param \Todaymade\Daux\Config $params
|
||||
* @param OutputInterface $output
|
||||
* @param int $width
|
||||
* @param bool $index_pages
|
||||
* @param string $base_url
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function generateRecursive(Directory $tree, $output_dir, GlobalConfig $config, $output, $width, $index_pages, $base_url = '')
|
||||
private function generateRecursive(Directory $tree, $output_dir, $params, $output, $width, $index_pages, $base_url = '')
|
||||
{
|
||||
DauxHelper::rebaseConfiguration($config, $base_url);
|
||||
DauxHelper::rebaseConfiguration($params, $base_url);
|
||||
|
||||
if ($base_url !== '' && !$config->hasEntryPage()) {
|
||||
$config->setEntryPage($tree->getFirstPage());
|
||||
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, $config, $output, $width, $index_pages, '../' . $base_url);
|
||||
$this->generateRecursive($node, $new_output_dir, $params, $output, $width, $index_pages, '../' . $base_url);
|
||||
|
||||
// Rebase configuration again as $config is a shared object
|
||||
DauxHelper::rebaseConfiguration($config, $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, $config, $index_pages) {
|
||||
function () use ($node, $output_dir, $key, $params, $index_pages) {
|
||||
if ($node instanceof Raw) {
|
||||
copy($node->getPath(), $output_dir . DIRECTORY_SEPARATOR . $key);
|
||||
|
||||
@ -175,12 +191,12 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
|
||||
$this->daux->tree->setActiveNode($node);
|
||||
|
||||
$generated = $this->generateOne($node, $config);
|
||||
$generated = $this->generateOne($node, $params);
|
||||
file_put_contents($output_dir . DIRECTORY_SEPARATOR . $key, $generated->getContent());
|
||||
if ($index_pages) {
|
||||
$this->indexed_pages[] = [
|
||||
'title' => $node->getTitle(),
|
||||
'text' => $this->sanitize($generated->getPureContent()),
|
||||
'text' => utf8_encode($this->strip_html_tags($generated->getPureContent())),
|
||||
'tags' => '',
|
||||
'url' => $node->getUrl(),
|
||||
];
|
||||
@ -192,9 +208,11 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entry $node
|
||||
* @param Config $params
|
||||
* @return \Todaymade\Daux\Format\Base\Page
|
||||
*/
|
||||
public function generateOne(Entry $node, GlobalConfig $config)
|
||||
public function generateOne(Entry $node, Config $params)
|
||||
{
|
||||
if ($node instanceof Raw) {
|
||||
return new RawPage($node->getPath());
|
||||
@ -204,11 +222,8 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
||||
return new ComputedRawPage($node);
|
||||
}
|
||||
|
||||
$config->setRequest($node->getUrl());
|
||||
$params['request'] = $node->getUrl();
|
||||
|
||||
$contentPage = ContentPage::fromFile($node, $config, $this->daux->getContentTypeHandler()->getType($node));
|
||||
$contentPage->templateRenderer = $this->templateRenderer;
|
||||
|
||||
return $contentPage;
|
||||
return ContentPage::fromFile($node, $params, $this->daux->getContentTypeHandler()->getType($node));
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTML;
|
||||
|
||||
use Todaymade\Daux\GeneratorHelper;
|
||||
|
||||
trait HTMLUtils
|
||||
{
|
||||
public function ensureEmptyDestination($destination)
|
||||
{
|
||||
if (is_dir($destination)) {
|
||||
GeneratorHelper::rmdir($destination);
|
||||
} else {
|
||||
mkdir($destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all files from $local to $destination.
|
||||
*
|
||||
* @param string $destination
|
||||
* @param string $local_base
|
||||
*/
|
||||
public function copyThemes($destination, $local_base)
|
||||
{
|
||||
mkdir($destination . DIRECTORY_SEPARATOR . 'themes');
|
||||
GeneratorHelper::copyRecursive(
|
||||
$local_base,
|
||||
$destination . DIRECTORY_SEPARATOR . 'themes'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Todaymade\Daux\Format\HTML;
|
||||
<?php namespace Todaymade\Daux\Format\HTML;
|
||||
|
||||
use League\Plates\Engine;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Todaymade\Daux\Config as GlobalConfig;
|
||||
use Todaymade\Daux\Daux;
|
||||
use Todaymade\Daux\Tree\Content;
|
||||
use Todaymade\Daux\Tree\Directory;
|
||||
@ -13,30 +9,20 @@ class Template
|
||||
{
|
||||
protected $engine;
|
||||
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param string $base
|
||||
* @param string $theme
|
||||
*/
|
||||
public function __construct(GlobalConfig $config)
|
||||
public function __construct($base, $theme)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getEngine(GlobalConfig $config)
|
||||
{
|
||||
if ($this->engine) {
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
$base = $config->getTemplates();
|
||||
$theme = $config->getTheme()->getTemplates();
|
||||
|
||||
// Use internal templates if no templates
|
||||
// dir exists in the working directory
|
||||
// Use internal templates or the ones in the phar
|
||||
// archive if no templates dir exists in the working directory
|
||||
if (!is_dir($base)) {
|
||||
if (is_dir(__DIR__ . '/../../../templates')) {
|
||||
$base = __DIR__ . '/../../../templates';
|
||||
} else {
|
||||
$base = 'phar://daux.phar/templates';
|
||||
}
|
||||
}
|
||||
|
||||
// Create new Plates instance
|
||||
@ -46,71 +32,41 @@ class Template
|
||||
}
|
||||
$this->engine->addFolder('theme', $theme, true);
|
||||
|
||||
Daux::writeln("Starting Template engine with basedir '$base' and theme folder '$theme'.", OutputInterface::VERBOSITY_VERBOSE);
|
||||
|
||||
$this->registerFunctions($this->engine);
|
||||
|
||||
return $this->engine;
|
||||
$this->registerFunctions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function render($name, array $data = [])
|
||||
{
|
||||
$engine = $this->getEngine($data['config']);
|
||||
|
||||
$engine->addData([
|
||||
'base_url' => $data['config']->getBaseUrl(),
|
||||
'base_page' => $data['config']->getBasePage(),
|
||||
$this->engine->addData([
|
||||
'base_url' => $data['params']['base_url'],
|
||||
'base_page' => $data['params']['base_page'],
|
||||
'page' => $data['page'],
|
||||
'params' => $data['config'], // legacy name for config
|
||||
'config' => $data['config'],
|
||||
'tree' => $data['config']['tree'],
|
||||
'params' => $data['params'],
|
||||
'tree' => $data['params']['tree'],
|
||||
]);
|
||||
|
||||
Daux::writeln("Rendering template '$name'", OutputInterface::VERBOSITY_VERBOSE);
|
||||
|
||||
return $engine->render($name, $data);
|
||||
return $this->engine->render($name, $data);
|
||||
}
|
||||
|
||||
protected function registerFunctions($engine)
|
||||
protected function registerFunctions()
|
||||
{
|
||||
$engine->registerFunction('get_navigation', function ($tree, $path, $current_url, $base_page, $mode) {
|
||||
$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);
|
||||
});
|
||||
|
||||
$engine->registerFunction('translate', function ($key) {
|
||||
$language = $this->config->getLanguage();
|
||||
|
||||
if (isset($this->engine->getData('page')['page'])) {
|
||||
$page = $this->engine->getData('page');
|
||||
if (!empty($page['page']['language'])) {
|
||||
$language = $page['page']['language'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->config->hasTranslationKey($language, $key)) {
|
||||
return $this->config->getTranslationKey($language, $key);
|
||||
}
|
||||
|
||||
if ($this->config->hasTranslationKey('en', $key)) {
|
||||
return $this->config->getTranslationKey('en', $key);
|
||||
}
|
||||
|
||||
return "Unknown key $key";
|
||||
});
|
||||
|
||||
$engine->registerFunction('get_breadcrumb_title', function ($page, $base_page) {
|
||||
$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 $value) {
|
||||
$title .= '<a href="' . $base_page . $value['url'] . '">' . $value['title'] . '</a>' . $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'] != '') {
|
||||
@ -132,9 +88,9 @@ class Template
|
||||
$icon = '<i class="Nav__arrow"> </i>';
|
||||
|
||||
if (array_key_exists('href', $entry)) {
|
||||
$link = '<a href="' . $entry['href'] . '" class="Nav__item__link">' . $icon . $entry['title'] . '</a>';
|
||||
$link = '<a href="' . $entry['href'] . '" class="folder">' . $icon . $entry['title'] . '</a>';
|
||||
} else {
|
||||
$link = '<a href="#" class="Nav__item__link Nav__item__link--nopage">' . $icon . $entry['title'] . '</a>';
|
||||
$link = '<a href="#" class="aj-nav folder">' . $icon . $entry['title'] . '</a>';
|
||||
}
|
||||
|
||||
$link .= $this->renderNavigation($entry['children']);
|
||||
@ -196,7 +152,6 @@ class Template
|
||||
|
||||
/**
|
||||
* @param string $separator
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSeparator($separator)
|
||||
|
@ -1,31 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTML;
|
||||
|
||||
use Todaymade\Daux\BaseConfig;
|
||||
|
||||
class Theme extends BaseConfig
|
||||
{
|
||||
public function getFonts()
|
||||
{
|
||||
return $this->getValue('fonts');
|
||||
}
|
||||
|
||||
public function getCSS()
|
||||
{
|
||||
return $this->getValue('css');
|
||||
}
|
||||
|
||||
public function getJS()
|
||||
{
|
||||
return $this->getValue('js');
|
||||
}
|
||||
|
||||
public function getFavicon()
|
||||
{
|
||||
return $this->getValue('favicon');
|
||||
}
|
||||
|
||||
public function getTemplates()
|
||||
{
|
||||
return $this->getValue('templates');
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTMLFile;
|
||||
|
||||
use RuntimeException;
|
||||
use Todaymade\Daux\Tree\Content;
|
||||
use Todaymade\Daux\Tree\Directory;
|
||||
|
||||
@ -21,9 +22,15 @@ class Book
|
||||
return '<style>' . file_get_contents('themes/daux_singlepage/css/main.min.css') . '</style>';
|
||||
}
|
||||
|
||||
protected function getPageUrl($page)
|
||||
protected function getSectionId(Content $node)
|
||||
{
|
||||
return 'file_' . str_replace('/', '_', $page->getUrl());
|
||||
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)
|
||||
@ -37,7 +44,7 @@ class Book
|
||||
|
||||
$nav[] = [
|
||||
'title' => $node->getTitle(),
|
||||
'href' => '#' . $this->getPageUrl($node),
|
||||
'href' => '#section_' . $this->getSectionId($node),
|
||||
];
|
||||
} elseif ($node instanceof Directory) {
|
||||
if (!$node->hasContent()) {
|
||||
@ -48,7 +55,7 @@ class Book
|
||||
|
||||
$nav[] = [
|
||||
'title' => $node->getTitle(),
|
||||
'href' => '#' . $this->getPageUrl($page_index),
|
||||
'href' => '#section_' . $this->getSectionId($page_index),
|
||||
'children' => $this->buildNavigation($node),
|
||||
];
|
||||
}
|
||||
@ -63,9 +70,9 @@ class Book
|
||||
foreach ($entries as $entry) {
|
||||
if (array_key_exists('children', $entry)) {
|
||||
if (array_key_exists('href', $entry)) {
|
||||
$link = '<a href="' . $entry['href'] . '" class="Nav__item__link--nopage">' . $entry['title'] . '</a>';
|
||||
$link = '<a href="' . $entry['href'] . '" class="folder">' . $entry['title'] . '</a>';
|
||||
} else {
|
||||
$link = '<a href="#" class="Nav__item__link Nav__item__link--nopage">' . $entry['title'] . '</a>';
|
||||
$link = '<a href="#" class="aj-nav folder">' . $entry['title'] . '</a>';
|
||||
}
|
||||
|
||||
$link .= $this->renderNavigation($entry['children']);
|
||||
@ -83,25 +90,25 @@ class Book
|
||||
{
|
||||
return '<h1>Table of Contents</h1>' .
|
||||
$this->renderNavigation($this->buildNavigation($this->tree)) .
|
||||
'</div><div class="PageBreak"> </div>';
|
||||
'</div><div class="page-break"> </div>';
|
||||
}
|
||||
|
||||
protected function generateCover()
|
||||
{
|
||||
return '<div>' .
|
||||
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="PageBreak"> </div>';
|
||||
'</div><div class="page-break"> </div>';
|
||||
}
|
||||
|
||||
protected function generatePages()
|
||||
{
|
||||
$content = '';
|
||||
foreach ($this->pages as $page) {
|
||||
$content .= '<a id="' . $this->getPageUrl($page['page']) . '"></a>';
|
||||
foreach ($this->pages as $section => $page) {
|
||||
$content .= '<a id="section_' . $section . '"></a>';
|
||||
$content .= '<h1>' . $page['page']->getTitle() . '</h1>';
|
||||
$content .= '<section class="s-content">' . $page['content'] . '</section>';
|
||||
$content .= '<div class="PageBreak"> </div>';
|
||||
$content .= '<section class="content">' . $page['content'] . '</section>';
|
||||
$content .= '<div class="page-break"> </div>';
|
||||
}
|
||||
|
||||
return $content;
|
||||
|
@ -11,23 +11,23 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
|
||||
{
|
||||
$content = parent::generatePage();
|
||||
|
||||
// Embed images
|
||||
//Embed images
|
||||
// We do it after generation so we can catch the images that were in html already
|
||||
return (new EmbedImages($this->config->getTree()))
|
||||
$content = (new EmbedImages($this->params['tree']))
|
||||
->embed(
|
||||
$content,
|
||||
$this->file,
|
||||
function ($src, array $attributes, Raw $file) {
|
||||
// TODO :: ignore absolute paths
|
||||
$content = base64_encode(file_get_contents($file->getPath()));
|
||||
$attr = '';
|
||||
foreach ($attributes as $name => $value) {
|
||||
$attr .= ' ' . $name . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
|
||||
}
|
||||
|
||||
// TODO :: handle other formats than PNG as well
|
||||
return "<img $attr src=\"data:image/png;base64,$content\"/>";
|
||||
}
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
<?php namespace Todaymade\Daux\Format\HTMLFile\ContentTypes\Markdown;
|
||||
|
||||
use League\CommonMark\Environment;
|
||||
|
||||
class CommonMarkConverter extends \Todaymade\Daux\Format\HTML\ContentTypes\Markdown\CommonMarkConverter
|
||||
{
|
||||
protected function getLinkRenderer(Environment $environment)
|
||||
{
|
||||
return new LinkRenderer($environment->getConfig('daux'));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user