Compare commits

..

25 Commits

Author SHA1 Message Date
Daniel Seifert 10e9e7039e
add common config settings to template variables 2020-05-28 08:58:33 +02:00
Daniel Seifert d863806bf3 change help center link 2019-09-24 09:56:26 +02:00
Daniel Seifert ae71e1ef56 remove duplicated version information in case of displayed version selector, add links static (default configuration will overridden) 2019-02-15 11:13:40 +01:00
Daniel Seifert 57a739e2aa change search pattern for d3 logo replacement
use "(D3)" instead of "D3"
2019-02-15 11:13:40 +01:00
Daniel Seifert 6a98b9a275 Merge remote-tracking branch 'remotes/origin/branch_0.7.3' into d3version
# Conflicts:
#	themes/d3/css/d3.css
2018-12-20 11:43:10 +01:00
Daniel Seifert 834f2bc854 Merge remote-tracking branch 'remotes/origin/branch_0.7.2' into branch_0.7.3
# Conflicts:
#	themes/d3/css/d3.css
2018-12-20 11:40:55 +01:00
Daniel Seifert a041be888a change headline format to smaller fontsize for mobile devices 2018-12-20 11:38:48 +01:00
Daniel Seifert ebd49e545d don't export templates and scss files 2018-07-20 22:10:45 +02:00
Daniel Seifert afb04ef056 don't export templates and scss files 2018-07-20 22:10:05 +02:00
Daniel Seifert 0d8db42cc6 changed wrong resource link 2018-07-20 21:00:35 +02:00
Daniel Seifert b513f88328 changed wrong resource link 2018-07-20 21:00:05 +02:00
Daniel Seifert 07433dd826 don't forget configuration in case of deeper source directory 2018-07-19 22:04:02 +02:00
Daniel Seifert c88f63c4cb don't forget configuration in case of deeper source directory 2018-07-19 22:03:35 +02:00
Daniel Seifert c367955a66 check for set variables 2018-07-19 20:32:16 +02:00
Daniel Seifert d73c036e5a check for set variables 2018-07-19 20:31:47 +02:00
Daniel Seifert 50caa843f3 add versionselector variable check 2018-07-19 20:27:13 +02:00
Daniel Seifert 2b037e75e1 add versionselector variable check 2018-07-19 20:26:25 +02:00
Daniel Seifert f8a6a64889 clean themes, note theme name change! 2018-07-11 23:17:02 +02:00
Daniel Seifert ee3cf6918e clean themes, note theme name change! 2018-07-11 23:16:07 +02:00
Daniel Seifert a2d3ec93a5 allow parent directory, add version selector in home page 2018-07-10 21:59:57 +02:00
Daniel Seifert e56a30650c allow parent directory, add version selector in home page 2018-07-10 21:57:31 +02:00
Daniel Seifert 416681f8bd fix selected 2018-07-10 00:58:58 +02:00
Daniel Seifert f760a04ee8 fix selected 2018-07-10 00:53:34 +02:00
Daniel Seifert 5f69f8c303 add versionselector option 2018-07-10 00:40:39 +02:00
Daniel Seifert 0b9243768a add versionselector option 2018-07-10 00:37:00 +02:00
188 changed files with 8824 additions and 15990 deletions

View File

@ -1,2 +0,0 @@
vendor
node_modules

View File

@ -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.

View File

@ -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.

View File

@ -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
View File

@ -5,12 +5,3 @@ node_modules
static static
/vendor /vendor
/build
.phpunit.result.cache
.php_cs.cache
coverage.clover
test-report.xml
/prettier.config.js
/.eslintrc.js
/stylelint.config.js

28
.php_cs
View File

@ -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)
;

31
.travis.yml Normal file
View File

@ -0,0 +1,31 @@
language: php
php:
- '7.1'
- '7.2'
- 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
before_deploy:
- bin/daux generate
deploy:
provider: pages
local_dir: static
skip_cleanup: true
github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard
on:
branch: master

View File

@ -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

View File

@ -1,31 +1,31 @@
FROM composer:1.10.5 AS composer FROM php:7-alpine
FROM php:7-stretch RUN apk info && apk add --no-cache unzip
RUN apt-get update && apt-get install -y libicu-dev git unzip
RUN docker-php-ext-configure intl \
&& docker-php-ext-install intl
RUN mkdir /daux && mkdir /build RUN mkdir /daux && mkdir /build
WORKDIR /daux WORKDIR /daux
COPY --from=composer /usr/bin/composer /usr/bin/composer
# Copy files # 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 bin/ /daux/bin/
COPY libs/ /daux/libs/ COPY libs/ /daux/libs/
COPY templates/ /daux/templates/ COPY templates/ /daux/templates/
COPY themes/ /daux/themes/ COPY themes/ /daux/themes/
COPY daux_libraries/ /daux/daux_libraries/ COPY tipuesearch/ /daux/tipuesearch/
COPY global.json /daux/global.json COPY global.json /daux/global.json
COPY composer.json /daux/composer.json
COPY composer.lock /daux/composer.lock
COPY index.php /daux/index.php COPY index.php /daux/index.php
# 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 RUN ln -s /daux/bin/daux /usr/local/bin/daux
WORKDIR /build WORKDIR /build

168
README.md
View File

@ -1,48 +1,51 @@
# Daux.io # Daux.io
[![Latest Version](https://img.shields.io/github/release/dauxio/daux.io.svg?style=flat-square)](https://github.com/dauxio/daux.io/releases) [![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) [![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) [![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) [![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/daux/daux.io.svg?style=flat-square)](https://packagist.org/packages/daux/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. **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 ## Features
- 100% Mobile Responsive * 100% Mobile Responsive
- CommonMark compliant (a Markdown specification) * CommonMark compliant (a Markdown specification)
- Supports Markdown tables * Supports Markdown tables
- Auto created homepage/landing page * Auto created homepage/landing page
- Auto Syntax Highlighting * Auto Syntax Highlighting
- Auto Generated Navigation * Auto Generated Navigation
- 4 Built-In Themes or roll your own * 4 Built-In Themes or roll your own
- Functional, Flat Design Style * Functional, Flat Design Style
- Shareable/Linkable SEO Friendly URLs * Shareable/Linkable SEO Friendly URLs
- Built On Bootstrap * Built On Bootstrap
- No Build Step * No Build Step
- Git/SVN Friendly * Git/SVN Friendly
- Supports Google Analytics and Piwik Analytics * Supports Google Analytics and Piwik Analytics
- Optional code float layout * Optional code float layout
- Static Output Generation * Static Output Generation
## Demos ## Demos
This is a list of sites using Daux.io: This is a list of sites using Daux.io:
- With a custom theme: - With a custom theme:
- [Crafty](https://swissquote.github.io/crafty) * [Pixolution flow](https://docs.pixolution.org)
- [Pixolution flow](https://docs.pixolution.org) \* [Soisy](https://doc.soisy.it/) * [Crafty](https://swissquote.github.io/crafty)
- [Vulkan Tutorial](https://vulkan-tutorial.com) \* [3Q](https://docs.3q.video/) * [Vulkan Tutorial](https://vulkan-tutorial.com)
- [The Advanced RSS Environment](https://thearsse.com/manual/) * [TrackJs](http://docs.trackjs.com)
- With the default Theme - With the default Theme
- [Daux.io](https://daux.io/) * [Daux.io](https://daux.io/)
_ [DoctrineWatcher](https://dsentker.github.io/WatcherDocumentation/) * [Gltn - An open-source word processor webapp](http://felkerdigitalmedia.com/gltn/docs/)
_ [DrupalGap](http://docs.drupalgap.org/8/) * [Invade & Annex 3 - An Arma 3 Co-operative Mission](http://ia3.ahoyworld.co.uk/)
- [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/) * [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
- [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee) * [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
- [Nuntius: A PHP framework for bots](https://roysegall.github.io/nuntius-bot/) * [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. 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.
@ -68,7 +71,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 : Or if you wish to use Docker, the start of the command will be :
```bash ```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 ## Run on a server
@ -97,18 +100,18 @@ You must use underscores instead of spaces. Here are some example file names and
**Good:** **Good:**
- 01_Getting_Started.md = Getting Started * 01_Getting_Started.md = Getting Started
- API_Calls.md = API Calls * API_Calls.md = API Calls
- 200_Something_Else-Cool.md = Something Else-Cool * 200_Something_Else-Cool.md = Something Else-Cool
- \_5_Ways_to_Be_Happy.md = 5 Ways To Be Happy * _5_Ways_to_Be_Happy.md = 5 Ways To Be Happy
**Bad:** **Bad:**
- File Name With Space.md = FAIL * File Name With Space.md = FAIL
## Sorting ## 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 ## Landing page
@ -116,9 +119,9 @@ If you want to create a beautiful landing page for your project, simply create a
```json ```json
{ {
"title": "Daux.io", "title": "Daux.io",
"tagline": "The Easiest Way To Document Your Project", "tagline": "The Easiest Way To Document Your Project",
"image": "app.png" "image": "app.png"
} }
``` ```
@ -134,46 +137,42 @@ 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. The `config.json` file is a simple JSON object that you can use to change some of the basic settings of the documentation.
### Title ### Title
Change the title bar in the docs Change the title bar in the docs
```json ```json
{ {
"title": "Daux.io" "title": "Daux.io"
} }
``` ```
### Themes ### Themes
We have 4 built-in Bootstrap themes. To use one of the themes, just set the `theme` option to one of the following: 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-blue
- daux-green * daux-green
- daux-navy * daux-navy
- daux-red * daux-red
```json ```json
{ {
"html": { "theme": "daux-green" } "html": { "theme": "daux-green" }
} }
``` ```
### More options ### More options
Many other options are available: Many other options are available:
- [Global options](http://daux.io/Configuration/index)
- [Global options](http://daux.io/Configuration/index) - [HTML Options](http://daux.io/Configuration/Html_export)
- [HTML Options](http://daux.io/Configuration/Html_export) - [Confluence options](http://daux.io/Configuration/Confluence_upload)
- [Confluence options](http://daux.io/Configuration/Confluence_upload)
## Running Remotely ## 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 ## Running Locally
There are several ways to run the docs 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. The recommended way is to run `daux serve` which will execute PHP's embedded server.
By default the server will run at: <a href="http://localhost:8085" target="_blank">http://localhost:8085</a> By default the server will run at: <a href="http://localhost:8085" target="_blank">http://localhost:8085</a>
@ -193,8 +192,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: If you have set up a local or remote IIS web site, you may need a `web.config` with:
- A rewrite configuration, for handling clean urls. * A rewrite configuration, for handling clean urls.
- A mime type handler for less files, if using a custom theme. * A mime type handler for less files, if using a custom theme.
### Clean URLs ### Clean URLs
@ -202,46 +201,47 @@ The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`:
```xml ```xml
<configuration> <configuration>
<system.webServer> <system.webServer>
<rewrite> <rewrite>
<rules> <rules>
<rule name="Main Rule" stopProcessing="true"> <rule name="Main Rule" stopProcessing="true">
<match url=".*" /> <match url=".*" />
<conditions logicalGrouping="MatchAll"> <conditions logicalGrouping="MatchAll">
<add <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
input="{REQUEST_FILENAME}" <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
matchType="IsFile" </conditions>
negate="true" <action type="Rewrite" url="index.php" appendQueryString="false" />
/> </rule>
<add </rules>
input="{REQUEST_FILENAME}" </rewrite>
matchType="IsDirectory" </system.webServer>
negate="true"
/>
</conditions>
<action
type="Rewrite"
url="index.php"
appendQueryString="false"
/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration> </configuration>
``` ```
To use clean URLs on IIS 6, you will need to use a custom URL rewrite module, such as [URL Rewriter](http://urlrewriter.net/). To use clean URLs on IIS 6, you will need to use a custom URL rewrite module, such as [URL Rewriter](http://urlrewriter.net/).
## 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 ## 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 ### 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 ## Support

View File

@ -17,22 +17,16 @@
], ],
"bin": ["bin/daux"], "bin": ["bin/daux"],
"require": { "require": {
"php": ">=7.2", "php": ">=7.1.3",
"guzzlehttp/guzzle": "~6.0", "guzzlehttp/guzzle": "~6.0",
"league/commonmark": "^1.0.0", "league/commonmark": "^0.15",
"league/plates": "~3.1", "league/plates": "~3.1",
"myclabs/deep-copy": "^1.5", "myclabs/deep-copy": "^1.5",
"scrivo/highlight.php": "^9.15", "symfony/console": "^4.0",
"symfony/console": "^5.0", "symfony/http-foundation": "^4.0",
"symfony/http-foundation": "^5.0", "symfony/process": "^4.0",
"symfony/mime": "^5.0", "webuni/commonmark-table-extension": "0.6.*",
"symfony/polyfill-intl-icu": "^1.10", "webuni/front-matter": "^1.0.0"
"symfony/process": "^5.0",
"webuni/front-matter": "^1.0.0",
"ext-json": "*"
},
"suggest":{
"ext-intl": "Allows to translate the modified at date"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -43,12 +37,7 @@
"justinwalsh/daux.io": "*" "justinwalsh/daux.io": "*"
}, },
"require-dev": { "require-dev": {
"mikey179/vfsstream": "^1.6" "phpunit/phpunit": "~5.7",
}, "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"
} }
} }

2388
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,67 +1,46 @@
module.exports = { module.exports = {
browsers: "defaults, not op_mini all",
presets: [ presets: [
"@swissquote/crafty-preset-babel", "@swissquote/crafty-preset-postcss",
"@swissquote/crafty-runner-rollup", "@swissquote/crafty-runner-gulp"
"@swissquote/crafty-preset-postcss",
"@swissquote/crafty-runner-gulp"
], ],
destination_css: ".", destination_css: "themes",
destination_js: ".",
stylelint_pattern: [ stylelint_pattern: [
"src/css/**/*.scss", "themes/daux/scss/**/*.scss",
"!*.min.css", "themes/daux_singlepage/scss/**/*.scss",
"!**/vendor/**/*.scss" "!*.min.css",
"!**/vendor/**/*.scss"
], ],
stylelint: { stylelint: {
rules: { rules: {
"swissquote/no-type-outside-scope": null, "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: { css: {
theme_blue: { "theme_blue": {
source: "src/css/theme_daux/theme-blue.scss", source: "themes/daux/scss/theme-blue.scss",
destination: "themes/daux/css/theme-blue.min.css", destination: "daux/css/theme-blue.min.css",
watch: ["src/css/**/*.scss"] watch: ["themes/daux/scss/**"]
}, },
theme_green: { "theme_green": {
source: "src/css/theme_daux/theme-green.scss", source: "themes/daux/scss/theme-green.scss",
destination: "themes/daux/css/theme-green.min.css", destination: "daux/css/theme-green.min.css",
watch: ["src/css/**/*.scss"] watch: ["themes/daux/scss/**"]
}, },
theme_navy: { "theme_navy": {
source: "src/css/theme_daux/theme-navy.scss", source: "themes/daux/scss/theme-navy.scss",
destination: "themes/daux/css/theme-navy.min.css", destination: "daux/css/theme-navy.min.css",
watch: ["src/css/**/*.scss"] watch: ["themes/daux/scss/**"]
}, },
theme_red: { "theme_red": {
source: "src/css/theme_daux/theme-red.scss", source: "themes/daux/scss/theme-red.scss",
destination: "themes/daux/css/theme-red.min.css", destination: "daux/css/theme-red.min.css",
watch: ["src/css/**/*.scss"] watch: ["themes/daux/scss/**"]
}, },
daux_singlepage: { "daux_singlepage": {
source: "src/css/theme_daux_singlepage/main.scss", source: "themes/daux_singlepage/scss/main.scss",
destination: "themes/daux_singlepage/css/main.min.css", destination: "daux_singlepage/css/main.min.css",
watch: ["src/css/**/*.scss"] watch: ["themes/daux_singlepage/scss/**"]
} }
},
postcss(crafty, config, bundle) {
// Add postcss-page-break
config.processor("postcss-page-break").before("autoprefixer");
} }
}; };

View File

@ -134,6 +134,14 @@ class d3DocumentRenderer implements BlockRendererInterface
$daux->initializeConfiguration(); $daux->initializeConfiguration();
$params = $daux->getParams(); $params = $daux->getParams();
foreach ($params as $varname => $varvalue) {
if (false == is_array($varvalue)) {
$pattern = '/{\$'.$varname.'}/mU';
$wholeDoc = preg_replace($pattern, $varvalue, $wholeDoc);
}
}
if (isset($params['variables'])) { if (isset($params['variables'])) {
$variables = $params['variables']; $variables = $params['variables'];
if (isset($variables) && is_array($variables) && count($variables)) { if (isset($variables) && is_array($variables) && count($variables)) {
@ -170,7 +178,7 @@ class d3TextRenderer implements InlineRendererInterface
$content = $htmlRenderer->escape($inline->getContent()); $content = $htmlRenderer->escape($inline->getContent());
$search = array( $search = array(
'D3', 'D³', 'D&sup3;' '(D3)', '()', '(D&sup3;)'
); );
$replace = "<i class='fab fa-d3 d3fa-color-blue'></i>"; $replace = "<i class='fab fa-d3 d3fa-color-blue'></i>";
$content = str_replace($search, $replace, $content); $content = str_replace($search, $replace, $content);

View File

@ -1,12 +0,0 @@
# Updating Highlight.js
This build of highlight.js contains all languages. to achieve this, go to : https://highlightjs.org/download/
And run the following snipped in the console:
```
$$("input[type=checkbox]").forEach(function(checkbox) { checkbox.checked=true; })
```
This will tick all boxes instead of doing it by hand.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@ services:
- phpserver - phpserver
phpserver: phpserver:
image: php:7.4-fpm image: php:7.0-fpm
working_dir: /var/www/ working_dir: /var/www/
volumes: volumes:
- ../:/var/www/ - ../:/var/www/

View File

@ -6,48 +6,43 @@
### For Authors ### For Authors
- [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md) * [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md)
- [Internal documentation links](01_Features/Internal_links.md) * [Internal documentation links](01_Features/Internal_links.md)
- [CommonMark compliant](01_Features/CommonMark_compliant.md) * [CommonMark compliant](01_Features/CommonMark_compliant.md)
- [Auto created homepage/landing page](01_Features/Landing_page.md) * [Auto created homepage/landing page](01_Features/Landing_page.md)
- [Multiple Output Formats](01_Features/Multiple_Output_Formats.md) * [Multiple Output Formats](01_Features/Multiple_Output_Formats.md)
- [Multiple Languages Support](01_Features/Multilanguage.md) * [Multiple Languages Support](01_Features/Multilanguage.md)
- [No Build Step](01_Features/Live_mode.md) * [No Build Step](01_Features/Live_mode.md)
- [Static Output Generation](01_Features/Static_Site_Generation.md) * [Static Output Generation](01_Features/Static_Site_Generation.md)
- [Table of Contents](01_Features/Table_of_contents.md) * [Table of Contents](01_Features/Table_of_contents.md)
### For Developers ### For Developers
- [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md) * [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md)
- [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md) * [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md)
- Full access to the internal API to create new pages programatically * Full access to the internal API to create new pages programatically
- Work with pages metadata * Work with pages metadata
### For Marketing ### For Marketing
- 100% Mobile Responsive * 100% Mobile Responsive
- 4 Built-In Themes or roll your own * 4 Built-In Themes or roll your own
- Functional, Flat Design Style * Functional, Flat Design Style
- Optional code float layout * Optional code float layout
- Shareable/Linkable SEO Friendly URLs * Shareable/Linkable SEO Friendly URLs
- Supports Google Analytics and Piwik Analytics * Supports Google Analytics and Piwik Analytics
## Demos ## Demos
This is a list of sites using Daux.io: This is a list of sites using Daux.io:
- With a custom theme: * [Daux.io](https://dauxio.github.io/)
- [Crafty](https://swissquote.github.io/crafty) * [DoctrineWatcher](https://dsentker.github.io/WatcherDocumentation/)
- [Pixolution flow](https://docs.pixolution.org) \* [Soisy](https://doc.soisy.it/) * [jDrupal](http://jdrupal.easystreet3.com/8/docs/)
- [Vulkan Tutorial](https://vulkan-tutorial.com) * [DrupalGap](http://docs.drupalgap.org/8/)
- [3Q](https://docs.3q.video/) * [Invade & Annex 3 - An Arma 3 Co-operative Mission](http://ia3.ahoyworld.co.uk/)
- With the default Theme * [Munee: Standalone PHP 5.3 Asset Optimisation & Manipulation](http://mun.ee)
- [Daux.io](https://daux.io/) * [ICADMIN: An admin panel powered by CodeIgniter.](http://istocode.com/shared/ic-admin/)
_ [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/)
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. 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.
@ -68,7 +63,7 @@ daux generate
You can then use the `daux` command line to generate your documentation. 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 #### Docker
@ -80,10 +75,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 Any parameter valid in the PHP version is valid in the Docker version
### Writing pages ### Writing pages
Creating new pages is very easy: Creating new pages is very easy:
1. Create a markdown file (`*.md` or `*.markdown`) 1. Create a markdown file (`*.md` or `*.markdown`)
2. Start writing 2. Start writing
@ -97,14 +92,14 @@ You must use underscores instead of spaces. Here are some example file names and
**Good:** **Good:**
- 01_Getting_Started.md = Getting Started * 01_Getting_Started.md = Getting Started
- API_Calls.md = API Calls * API_Calls.md = API Calls
- 200_Something_Else-Cool.md = Something Else-Cool * 200_Something_Else-Cool.md = Something Else-Cool
- \_5_Ways_to_Be_Happy.md = 5 Ways To Be Happy * _5_Ways_to_Be_Happy.md = 5 Ways To Be Happy
**Bad:** **Bad:**
- File Name With Space.md = FAIL * File Name With Space.md = FAIL
### See your pages ### See your pages
@ -128,9 +123,9 @@ Upload your files to an apache / nginx server and see your documentation
Daux.io is extendable and comes by default with three export formats: Daux.io is extendable and comes by default with three export formats:
- Export to HTML, same as the website, but can be hosted without PHP. - Export to HTML, same as the website, but can be hosted without PHP.
- Export all documentation in a single HTML page - Export all documentation in a single HTML page
- Upload to your Atlassian Confluence server. - Upload to your Atlassian Confluence server.
[See a detailed feature comparison matrix](01_Features/Multiple_Output_Formats.md) [See a detailed feature comparison matrix](01_Features/Multiple_Output_Formats.md)
@ -144,13 +139,20 @@ Now that you got the basics, you can also [see what you can configure](05_Config
## PHP Requirements ## 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 ### 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 ## Support

View File

@ -1,14 +1,17 @@
As we support CommonMark, a broad range of markdown features is available to you. 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. 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: The above header should be an H2 tag. Now, for a list of fruits:
- Red Apples * Red Apples
- Purple Grapes * Purple Grapes
- Green Kiwifruits * Green Kiwifruits
Let's get crazy: Let's get crazy:
@ -24,17 +27,18 @@ Let's get crazy:
What about some code **in** a list? That's insane, right? What about some code **in** a list? That's insane, right?
1. In Ruby you can map like this: 1. In Ruby you can map like this:
['a', 'b'].map { |x| x.uppercase } ['a', 'b'].map { |x| x.uppercase }
2. In Rails, you can do a shortcut: 2. In Rails, you can do a shortcut:
['a', 'b'].map(&:uppercase) ['a', 'b'].map(&:uppercase)
Some people seem to like definition lists 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? 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?
&copy; 2004 Foo Corporation &copy; 2004 Foo Corporation
</div> </div>
## Set in stone Set in stone
------------
Preformatted blocks are useful for ASCII art: Preformatted blocks are useful for ASCII art:
@ -68,7 +73,8 @@ Preformatted blocks are useful for ASCII art:
___|_____________ ___|_____________
</pre> </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: 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 > 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. > as I should be in coming up with one.
## Table for two Table for two
-------------
| ID | Name | Rank | ID | Name | Rank
| --- | :----------------: | ----------------: | ---|:------:|------:
| 1 | Tom Preston-Werner | Awesome | 1 | Tom Preston-Werner | Awesome
| 2 | Albert Einstein | Nearly as awesome | 2 | Albert Einstein | Nearly as awesome
## Crazy linking action Crazy linking action
--------------------
I get 10 times more traffic from [Google][1] than from I get 10 times more traffic from [Google] [1] than from
[Yahoo][2] or [MSN][3]. [Yahoo] [2] or [MSN] [3].
[1]: http://google.com/ "Google" [1]: http://google.com/ "Google"
[2]: http://search.yahoo.com/ "Yahoo Search" [2]: http://search.yahoo.com/ "Yahoo Search"
[3]: http://search.msn.com/ "MSN Search" [3]: http://search.msn.com/ "MSN Search"
## Images Images
------
Here's an image. 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. 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).*

View File

@ -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. 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. The value has to be the path to the root of your documentation folder in your repository.
@ -6,11 +7,12 @@ In the value you see below, Daux's documentation is in the `docs` folder in the
Daux.io will handle the rest Daux.io will handle the rest
```json ```json
{ {
"html": { "html": {
"edit_on_github": "dauxio/daux.io/blob/master/docs" "edit_on_github": "dauxio/daux.io/blob/master/docs"
} }
} }
``` ```
@ -20,13 +22,14 @@ 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: 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 ```json
{ {
"html": { "html": {
"edit_on": { "edit_on": {
"name": "Bitbucket", "name": "Bitbucket",
"basepath": "https://bitbucket.org/dauxio/daux.io/src/master/docs" "basepath": "https://bitbucket.org/dauxio/daux.io/src/master/docs"
}
} }
}
} }
``` ```

View File

@ -2,9 +2,9 @@ If you want to create a beautiful landing page for your project, create a `_inde
```json ```json
{ {
"title": "Daux.io", "title": "Daux.io",
"tagline": "The Easiest Way To Document Your Project", "tagline": "The Easiest Way To Document Your Project",
"image": "app.png" "image": "app.png"
} }
``` ```
@ -14,8 +14,8 @@ To disable the automatic landing page, you can set `auto_landing` to false in th
```json ```json
{ {
"html": { "html": {
"auto_landing": false "auto_landing": false
} }
} }
``` ```

View File

@ -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> 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 ## Running Remotely
### Clean URLs configuration ### Clean URLs configuration
@ -19,15 +20,15 @@ To enable the same, set the toggle in the `config.json` file in the `/docs` fold
```json ```json
{ {
"live": { "live": {
"clean_urls": true "clean_urls": true
} }
} }
``` ```
### Apache ### 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. 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: If you have set up a local or remote IIS web site, you may need a `web.config` with:
- A rewrite configuration, for handling clean urls. * A rewrite configuration, for handling clean urls.
- A mime type handler for less files, if using a custom theme. * A mime type handler for less files, if using a custom theme.
### Clean URLs ### Clean URLs
@ -78,32 +79,20 @@ The `web.config` needs an entry for `<rewrite>` under `<system.webServer>`:
```xml ```xml
<configuration> <configuration>
<system.webServer> <system.webServer>
<rewrite> <rewrite>
<rules> <rules>
<rule name="Main Rule" stopProcessing="true"> <rule name="Main Rule" stopProcessing="true">
<match url=".*" /> <match url=".*" />
<conditions logicalGrouping="MatchAll"> <conditions logicalGrouping="MatchAll">
<add <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
input="{REQUEST_FILENAME}" <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
matchType="IsFile" </conditions>
negate="true" <action type="Rewrite" url="index.php" appendQueryString="false" />
/> </rule>
<add </rules>
input="{REQUEST_FILENAME}" </rewrite>
matchType="IsDirectory" </system.webServer>
negate="true"
/>
</conditions>
<action
type="Rewrite"
url="index.php"
appendQueryString="false"
/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration> </configuration>
``` ```
@ -125,4 +114,4 @@ 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` 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` You can access your documentation at `localhost:8000`

View File

@ -4,12 +4,11 @@ Add this to your config.json :
```json ```json
{ {
"languages": { "en": "English", "de": "German" } "languages": { "en": "English", "de": "German" }
} }
``` ```
You will the need separate directories for each language in `docs/` folder. You will the need separate directories for each language in `docs/` folder.
``` ```
├── docs/ ├── docs/
│ ├── _index.md │ ├── _index.md

View File

@ -1,21 +1,21 @@
Daux.io is extendable and comes by default with three export formats: Daux.io is extendable and comes by default with three export formats:
- Export to HTML - Export to HTML
- Export all documentation in a single HTML page - Export all documentation in a single HTML page
- Upload to your Atlassian Confluence server - Upload to your Atlassian Confluence server
## Feature Matrix ## Feature Matrix
| Feature | HTML | Single Page HTML | Confluence | Feature | HTML | Single Page HTML | Confluence
| -----------------------: | :--: | :--------------: | :-------------------------: | --------------:|:----:|:----------------:|:----------:
| Multilanguage | √ | X (Planned) | X | Multilanguage | √ | X (Planned) | X
| Landing Pages | √ | X | X | Landing Pages | √ | X | X
| Index Pages | √ | √ | √ | Index Pages | √ | √ | √
| Internal Links | √ | X (Planned) | √ | Internal Links | √ | X (Planned) | √
| Code Highlight | √ | X (Planned) | √ (Using macros) | Code Highlight | √ | X (Planned) | √ (Using macros)
| Live Mode | √ | X | X | Live Mode | √ | X | X
| Pages Ordering | √ | √ | X (API Limitation) | Pages Ordering | √ | √ | X (API Limitation)
| Google / Piwik analytics | | √ | √ (Configured on Conflence) | Google / Piwik analytics | √ | √ | √ (Configured on Conflence)
## Confluence Example ## Confluence Example

View File

@ -1,3 +1,4 @@
## Navigation ## Navigation
The navigation is generated automatically with all pages that end with `.md` or `.markdown` The navigation is generated automatically with all pages that end with `.md` or `.markdown`
@ -11,21 +12,21 @@ For example, `/docs/02_Examples` has a landing page for that section since there
## Sorting ## 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`. 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. 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` 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 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. 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. 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 `+`. It works the same for files prefixed with `+`.
Page order priorities are like this: Page order priorities are like this:
- `+` in front of the filename and numbers in front - `+` in front of the filename and numbers in front
- `+` in front of the filename - `+` in front of the filename
- The index page - The index page
- Numbers in the front - Numbers in the front
- Pages without prefix - Pages without prefix
- `-` in front of the filename and numbers in front - `-` in front of the filename and numbers in front
- `-` in front of the filename - `-` in front of the filename

View File

@ -6,8 +6,8 @@ To enable the generated search, you can set `search` to true in the `html` secti
```json ```json
{ {
"html": { "html": {
"search": true "search": true
} }
} }
``` ```

View File

@ -1,8 +1,9 @@
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 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 :
- Single page HTML output
- Atlassian Confluence upload - HTML output
- Single page HTML output
- Atlassian Confluence upload
Generating a complete set of pages, with navigation Generating a complete set of pages, with navigation
@ -12,7 +13,7 @@ daux --destination=[Output Directory Relative Direction]
## Options ## Options
For more options, run For more options, run
```bash ```bash
daux generate --help daux generate --help
@ -31,7 +32,7 @@ daux --format=html
### Specify a processor ### Specify a processor
A processor can be specified through the `--processor` option, this should be the name of a class inside the `Todaymade\Daux\Extension` namespace. A processor can be specified through the `--processor` option, this should be the name of a class inside the `Todaymade\Daux\Extension` namespace.
By running : By running :

View File

@ -18,8 +18,10 @@ You can enable this feature in your configuration
```json ```json
{ {
"html": { "html": {
"auto_toc": true "auto_toc": true
} }
} }
``` ```

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,2 @@
### This is a landing page for the Examples section ### 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.
Adding a landing page is pretty simple, all you need to do is add an "index.md" file to the related folder.

View File

@ -1,23 +1,21 @@
**Table of contents** __Table of contents__
[TOC] [TOC]
## Configuring the connection ## 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. 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 ```json
{ {
"confluence": { "confluence": {
"base_url": "http://my_confluence_server.com/", "base_url": "http://my_confluence_server.com/",
"user": "my_username", "user" : "my_username",
"pass": "my_password" "pass" : "my_password"
} }
} }
``` ```
## Where to upload ## Where to upload
Now that the connection is defined, you need to tell it where you want your documentation to be uploaded. 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. 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.
@ -26,10 +24,10 @@ You can obtain the `ancestor_id` id by editing the page you want to define as a
```json ```json
{ {
"confluence": { "confluence": {
"space_id": "my_space", "space_id": "my_space",
"ancestor_id": 50370632 "ancestor_id": 50370632
} }
} }
``` ```
@ -38,22 +36,20 @@ 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. You can use that when you're uploading your documentation to the root of a Confluence Space or if your page already exists.
## Prefix ## Prefix
Because confluence can't have two pages with the same name in a space, I recommend you define a prefix for your pages. Because confluence can't have two pages with the same name in a space, I recommend you define a prefix for your pages.
```json ```json
{ {
"confluence": { "prefix": "DAUX -" } "confluence": { "prefix": "DAUX -" }
} }
``` ```
## Update threshold ## 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. 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 ```json
{ {
"confluence": { "update_threshold": 1 } "confluence": { "update_threshold": 1 }
} }
``` ```
@ -63,22 +59,22 @@ By default the threshold is 2%.
Setting the value to `0` disables the feature altogether. 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. 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. By default, it will inform you that some pages aren't needed anymore and you can delete them by hand.
```json ```json
{ {
"confluence": { "delete": true } "confluence": { "delete": true }
} }
``` ```
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` with the `--delete` flag) you tell the generator that it can safely delete the pages.
## Information message
## Information message
When you create your page. there is no indication that the upload process will override the content of the pages. 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. It happens sometimes that users edit the pages to add / fix an information.
@ -87,9 +83,9 @@ You can add a text in a "information" macro on top of the document by setting th
```json ```json
{ {
"confluence": { "confluence": {
"header": "These pages are updated automatically, your changes will be overriden." "header": "These pages are updated automatically, your changes will be overriden."
} }
} }
``` ```

View File

@ -1,26 +1,24 @@
**Table of contents** __Table of contents__
[TOC] [TOC]
## Analytics ## Analytics
### Google Analytics ### Google Analytics
This will embed the google analytics tracking code. This will embed the google analytics tracking code.
```json ```json
{ {
"html": { "google_analytics": "UA-XXXXXXXXX-XX" } "html": { "google_analytics": "UA-XXXXXXXXX-XX" }
} }
``` ```
### Piwik Analytics ### Piwik Analytics
This will embed the piwik tracking code. This will embed the piwik tracking code.
```json ```json
{ {
"html": { "piwik_analytics": "my-url-for-piwik.com" } "html": { "piwik_analytics": "my-url-for-piwik.com" }
} }
``` ```
@ -28,130 +26,131 @@ You can Also give a specific Piwik ID as well.
```json ```json
{ {
"html": { "piwik_analytics_id": "43" } "html": { "piwik_analytics_id": "43" }
}
```
### Plausible Analytics
This will embed the https://plausible.io/ tracking code.
```json
{
"html": { "plausible_domain": "daux.io" }
} }
``` ```
## Automatic Table of Contents ## 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) 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. You can add buttons to the landing page.
```json ```json
{ {
"html": { "html": {
"buttons": { "buttons": {
"My Website": "http://example.com" "My Website": "http://example.com"
}
} }
}
} }
``` ```
## Breadcrumb titles ## Breadcrumb titles
Daux.io provides the option to present page titles as breadcrumb navigation.
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 ```json
{ {
"html": { "html": {
"breadcrumbs": true, "breadcrumbs": true,
"breadcrumb_separator": " > " "breadcrumb_separator" : " > "
} }
}
```
## 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 ## Date Modified
By default, daux.io will display the last modified time as reported by the system underneath the title for each document.
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 ```json
{ {
"html": { "date_modified": false } "html": { "date_modified": false }
}
```
If you want to use the last modified time you can set the [format](http://php.net/manual/function.date.php) with the `date_modified_format` option.
```json
{
"html": {
"date_modified": true,
"date_modified_format": "l, F j, Y g:i A"
}
} }
``` ```
## GitHub Repo ## GitHub Repo
Add a 'Fork me on GitHub' ribbon. Add a 'Fork me on GitHub' ribbon.
```json ```json
{ {
"html": { "repo": "dauxio/daux.io" } "html": { "repo": "dauxio/daux.io" }
} }
``` ```
## Inherit Index ## 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. This feature will instructs the navigation generator to seek the first available file to use when there is no index in a folder.
```json ```json
{ {
"html": { "inherit_index": true } "html": { "inherit_index": true }
} }
``` ```
## Jump buttons ## Jump buttons
You can have previous/next buttons on each page. You can have previous/next buttons on each page.
They can be disabled by setting `jump_buttons` to `false`. They can be disabled by setting `jump_buttons` to `false`.
```json ```json
{ {
"html": { "jump_buttons": false } "html": { "jump_buttons": false }
} }
``` ```
## Landing page ## Landing page
The automatic landing page can be disabled through the `auto_landing` option, read about it [here](../01_Features/Landing_page.md)
The automatic landing page can be disabled through the `auto_landing` option, read about it [here](../01_Features/Landing_page.md)
## Links ## Links
Include custom links in the sidebar. Include custom links in the sidebar.
```json ```json
{ {
"html": { "html": {
"links": { "links": {
"GitHub Repo": "https://github.com/dauxio/daux.io", "GitHub Repo": "https://github.com/dauxio/daux.io",
"Help/Support/Bugs": "https://github.com/dauxio/daux.io/issues", "Help/Support/Bugs": "https://github.com/dauxio/daux.io/issues",
"Made by Todaymade": "http://todaymade.com" "Made by Todaymade": "http://todaymade.com"
}
} }
}
} }
``` ```
## Search ## Search
Daux has an embedded search engine read all about it [here](../01_Features/Search.md) Daux has an embedded search engine read all about it [here](../01_Features/Search.md)
## Themes ## Themes
We have 4 built-in Bootstrap themes. To use one of the themes, just set the `theme` option to one of the following: 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-blue
- daux-green * daux-green
- daux-navy * daux-navy
- daux-red * daux-red
```json ```json
{ {
"html": { "theme": "daux-blue" } "html": { "theme": "daux-blue" }
} }
``` ```
@ -159,27 +158,25 @@ To use a custom theme, just copy over the theme folder into the `themes` directo
```json ```json
{ {
"html": { "theme": "new-theme" } "html": { "theme": "new-theme" }
} }
``` ```
## Toggling Code Blocks ## Toggling Code Blocks
Some users might wish to hide the code blocks & view just the documentation.
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. By setting the `toggle_code` property to `true`, you can offer a toggle button on the page.
```json ```json
{ {
"html": { "toggle_code": true } "html": { "toggle_code": true }
} }
``` ```
## Twitter ## Twitter
Include twitter follow buttons in the sidebar. Include twitter follow buttons in the sidebar.
```json ```json
{ {
"html": { "twitter": ["justin_walsh", "todaymade"] } "html": { "twitter": ["justin_walsh", "todaymade"] }
} }
``` ```

View File

@ -1,100 +1,91 @@
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. 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] [TOC]
### Title ### Title
Change the title bar in the docs Change the title bar in the docs
```json ```json
{ {
"title": "Daux.io" "title": "Daux.io"
} }
``` ```
### Tagline ### Tagline
Change the tagline bar in the docs Change the tagline bar in the docs
```json ```json
{ {
"tagline": "The Easiest Way To Document Your Project" "tagline": "The Easiest Way To Document Your Project"
} }
``` ```
### Author ### Author
Change the documentation's author Change the documentation's author
```json ```json
{ {
"author": "Stéphane Goetz" "author": "Stéphane Goetz"
} }
``` ```
### Image ### Image
An image to show on the landing page. A relative path from the documentation root. An image to show on the landing page. A relative path from the documentation root.
```json ```json
{ {
"image": "image.png" "image": "image.png"
} }
``` ```
### Format ### Format
Change the output format. It is recommended you set only formats that support the live mode as this will also 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 be read by the integrated web server. And you set the other formats (like confluence) only by command line
```json ```json
{ {
"format": "html" "format": "html"
} }
``` ```
- **html** with [its options](./Html_export.md) - __html__ with [its options](./Html_export.md)
- **confluence** with [its options](./Confluence_upload.md) - __confluence__ with [its options](./Confluence_upload.md)
Available formats are **HTML** and **Confluence** Available formats are __HTML__ and __Confluence__
### Ignore ### Ignore
Set custom files and entire folders to ignore within your `/docs` folder. For files make sure to include the file extension in the name. For both files and folders, names are case-sensitive. Set custom files and entire folders to ignore within your `/docs` folder. For files make sure to include the file extension in the name. For both files and folders, names are case-sensitive.
```json ```json
{ {
"ignore": { "ignore": {
"files": ["Work_In_Progress.md"], "files": ["Work_In_Progress.md"],
"folders": ["99_Not_Ready"] "folders": ["99_Not_Ready"]
} }
} }
``` ```
### Timezone ### Timezone
If your server does not have a default timezone set in php.ini, it may return errors when it tries to generate the last modified date/time for docs. To fix these errors, specify a timezone in your config file. Valid options are available in the [PHP Manual](http://php.net/manual/en/timezones.php). If your server does not have a default timezone set in php.ini, it may return errors when it tries to generate the last modified date/time for docs. To fix these errors, specify a timezone in your config file. Valid options are available in the [PHP Manual](http://php.net/manual/en/timezones.php).
```json ```json
{ {
"timezone": "America/Los_Angeles" "timezone": "America/Los_Angeles"
} }
``` ```
### Multi-language ### Multi-language
Enables multi-language support which needs separate directories for each language in `docs/` folder. Enables multi-language support which needs separate directories for each language in `docs/` folder.
```json ```json
{ {
"languages": { "en": "English", "de": "German" } "languages": {"en": "English", "de": "German"}
} }
``` ```
Directory structure: Directory structure:
``` ```
├── docs/ ├── docs/
│ ├── _index.md │ ├── _index.md
@ -122,7 +113,7 @@ You can change the default language with the `language` option.
```json ```json
{ {
"language": "fr" "language": "fr"
} }
``` ```
@ -133,28 +124,31 @@ A string that isn't found will fall back to english.
```json ```json
{ {
"strings": { "strings": {
"fr": { "fr": {
"CodeBlocks_show": "Afficher le code", "CodeBlocks_title": "Afficher le code",
"Search_placeholder": "Rechercher...", "CodeBlocks_hide": "Non",
"Link_previous": "Précédent", "CodeBlocks_below": "En Dessous",
"Link_next": "Suivant", "CodeBlocks_inline": "A côté",
"Edit_on": "Editer sur :name:", "CodeBlocks_show": "Afficher le code",
"View_on_github": "Voir sur GitHub", "Search_placeholder": "Rechercher...",
"View_documentation": "Voir la Documentation" "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 ### 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. 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). More information on how to create a Processor can be found [here](!For_Developers/Creating_a_Processor).
```json ```json
{ {
"processor": "MyProcessor" "processor": "MyProcessor"
} }
``` ```

View File

@ -7,7 +7,7 @@ The main advantage, is that you can run it with the source or with `daux` indepe
Next to your `docs` directory, you can create a `daux` directory that can contain your Processor. Next to your `docs` directory, you can create a `daux` directory that can contain your Processor.
The classes must respect the PSR-4 Naming convention. And have `\Todaymade\Daux\Extension` as a base namespace. The classes must respect the PSR-4 Naming convention. And have `\Todaymade\Daux\Extension` as a base namespace.
By default, we created a `daux/Processor.php` file to get you started. By default, we created a `daux/Processor.php` file to get you started.
## A quick test ? ## A quick test ?
@ -40,7 +40,7 @@ There are a few methods that you can override to add some
By default, Daux.io parses your directory to find pages. but, for a reason or another, you might want to programmatically add some pages. By default, Daux.io parses your directory to find pages. but, for a reason or another, you might want to programmatically add some pages.
This can be done with: This can be done with:
```php ```php
public function manipulateTree(Root $root) public function manipulateTree(Root $root)
@ -65,7 +65,7 @@ Both methods `getOrCreateDir` and `getOrCreatePage` take two parameters : `paren
The page will automatically be treated as markdown and converted like a normal page. The page will automatically be treated as markdown and converted like a normal page.
If you create a new ContentType, like let's say LaTeX, you would set the title `My Page.tex` it will keep the title `My Page` and use your renderer. If you create a new ContentType, like let's say LaTeX, you would set the title `My Page.tex` it will keep the title `My Page` and use your renderer.
If the extension is not mapped to a Generator, it will simply create the file as-is without manipulation. If the extension is not mapped to a Generator, it will simply create the file as-is without manipulation.
### Extend the Markdown Generator ### Extend the Markdown Generator
@ -82,9 +82,9 @@ See the details on [CommonMark's website](http://commonmark.thephpleague.com/cus
### Add new generators ### Add new generators
You can add new generators to Daux.io and use them right away, they must implement the You can add new generators to Daux.io and use them right away, they must implement the
`\Todaymade\Daux\Format\Base\Generator` interface and if you want to use the live mode with your generator `\Todaymade\Daux\Format\Base\Generator` interface and if you want to use the live mode with your generator
you have to implement `\Todaymade\Daux\Format\Base\LiveGenerator`. you have to implement `\Todaymade\Daux\Format\Base\LiveGenerator`.
```php ```php
public function addGenerators() public function addGenerators()
@ -92,3 +92,4 @@ public function addGenerators()
return ['custom_generator' => '\Todaymade\Daux\Extension\MyNewGenerator']; return ['custom_generator' => '\Todaymade\Daux\Extension\MyNewGenerator'];
} }
``` ```

View File

@ -2,56 +2,49 @@ 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. 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 ## `config.json` options
Here is an example `config.json` file : Here is an example `config.json` file :
```json ```json
{ {
"favicon": "<theme_url>img/favicon.png", "favicon": "<theme_url>img/favicon.png",
"css": ["<theme_url>css/theme.min.css"], "css": ["<theme_url>css/theme.min.css"],
"js": [], "js": [],
"fonts": [ "fonts": ["https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700&subset=latin,cyrillic-ext,cyrillic"],
"https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700&subset=latin,cyrillic-ext,cyrillic" "variants": {
], "blue": {
"variants": { "favicon": "<theme_url>img/favicon-blue.png",
"blue": { "css": ["<theme_url>css/theme-blue.min.css"]
"favicon": "<theme_url>img/favicon-blue.png", },
"css": ["<theme_url>css/theme-blue.min.css"] "green": {
}, "favicon": "<theme_url>img/favicon-green.png",
"green": { "css": ["<theme_url>css/theme-green.min.css"]
"favicon": "<theme_url>img/favicon-green.png",
"css": ["<theme_url>css/theme-green.min.css"]
}
} }
}
} }
``` ```
There are five options : There are five options :
- **`favicon`**: The URL to your favicon - __`favicon`__: The URL to your favicon
- **`css`**: An array of CSS Stylesheets to add to the page - __`css`__: An array of CSS Stylesheets to add to the page
- **`js`**: An array of JavaScript files to load - __`js`__: An array of JavaScript files to load
- **`fonts`**: An array of Font sources, these are added as stylesheets - __`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`) - __`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.
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. This is automatically substituted with the final url to the theme when generating the final page.
There are two possible substitutions : There are two possible substitutions :
- __`<theme_url>`__: The url to the current theme
- **`<theme_url>`**: The url to the current theme - __`<base_url>`__: The url to the documentation root
- **`<base_url>`**: The url to the documentation root
## Theme variants ## Theme variants
Like the default Daux.io theme, you might want to provide variants of your theme. Like the default Daux.io theme, you might want to provide variants of your theme.
In the example before, there were two variants : blue and green. In the example before, there were two variants : blue and green.
The configuration of a variant is added to the configuration of the main theme, it doesn't replace it. The configuration of a variant is added to the configuration of the main theme, it doesn't replace it.
@ -70,10 +63,10 @@ Change the `theme` option inside `html`
```json ```json
{ {
"themes_directory": "/home/user/themes", "themes_directory": "/home/user/themes",
"html": { "html": {
"theme": "{theme}-{variant}" "theme": "{theme}-{variant}"
} }
} }
``` ```
@ -92,12 +85,11 @@ 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. 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 : By default, we have the following templates :
- `content.php`: The content page.
- `content.php`: The content page. - `home.php`: The landing page.
- `home.php`: The landing page. - `error.php`: The page to show when a page is not found or some other error happened.
- `error.php`: The page to show when a page is not found or some other error happened. - `partials/navbar_content.php`: The content of the top navigation bar.
- `partials/navbar_content.php`: The content of the top navigation bar. - `partials/google_analytics.php`: The script to load Google Analytics.
- `partials/google_analytics.php`: The script to load Google Analytics. - `partials/piwik_analytics.php`: The script to load Piwik Analytics.
- `partials/piwik_analytics.php`: The script to load Piwik Analytics. - `layout/00_layout.php`: The master template, containing the `<html>` tag.
- `layout/00_layout.php`: The master template, containing the `<html>` tag. - `layout/05_page.php`: The page layout, with left navigation.
- `layout/05_page.php`: The page layout, with left navigation.

View File

@ -13,37 +13,37 @@
#### For Authors #### For Authors
- [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md) * [Auto Generated Navigation / Page sorting](01_Features/Navigation_and_Sorting.md)
- [Internal documentation links](01_Features/Internal_links.md) * [Internal documentation links](01_Features/Internal_links.md)
- [CommonMark compliant](01_Features/CommonMark_compliant.md) * [CommonMark compliant](01_Features/CommonMark_compliant.md)
- [Auto created homepage/landing page](01_Features/Landing_page.md) * [Auto created homepage/landing page](01_Features/Landing_page.md)
- [Multiple Output Formats](01_Features/Multiple_Output_Formats.md) * [Multiple Output Formats](01_Features/Multiple_Output_Formats.md)
- [Multiple Languages Support](01_Features/Multilanguage.md) * [Multiple Languages Support](01_Features/Multilanguage.md)
- [No Build Step](01_Features/Live_mode.md) * [No Build Step](01_Features/Live_mode.md)
- [Static Output Generation](01_Features/Static_Site_Generation.md) * [Static Output Generation](01_Features/Static_Site_Generation.md)
- [Table of Contents](01_Features/Table_of_contents.md) * [Table of Contents](01_Features/Table_of_contents.md)
</div> </div>
<div class="Row__third"> <div class="Row__third">
#### For Developers #### For Developers
- [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md) * [Auto Syntax Highlighting](01_Features/Auto_Syntax_Highlight.md)
- [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md) * [Extend Daux.io with Processors](01_For_Developers/Creating_a_Processor.md)
- Full access to the internal API to create new pages programatically * Full access to the internal API to create new pages programatically
- Work with pages metadata * Work with pages metadata
</div> </div>
<div class="Row__third"> <div class="Row__third">
#### For Marketing #### For Marketing
- 100% Mobile Responsive * 100% Mobile Responsive
- 4 Built-In Themes or roll your own * 4 Built-In Themes or roll your own
- Functional, Flat Design Style * Functional, Flat Design Style
- Optional code float layout * Optional code float layout
- Shareable/Linkable SEO Friendly URLs * Shareable/Linkable SEO Friendly URLs
- Supports Google Analytics and Piwik Analytics * Supports Google Analytics and Piwik Analytics
</div> </div>
</div> </div>
@ -52,7 +52,7 @@
### Installation and usage ### Installation and usage
If you have **PHP** and Composer installed If you have __PHP__ and Composer installed
```bash ```bash
composer global require daux/daux.io composer global require daux/daux.io
@ -61,7 +61,7 @@ composer global require daux/daux.io
daux generate daux generate
``` ```
Or if you wish to use **Docker** Or if you wish to use __Docker__
```bash ```bash
# Next to your `docs` folder, run # 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&amp;guid=ON&amp;script=0"/>
</div>
</noscript>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

@ -16,19 +16,20 @@
"breadcrumb_separator": "Chevrons", "breadcrumb_separator": "Chevrons",
"toggle_code": true, "toggle_code": true,
"date_modified": true, "date_modified": true,
"date_modified_format": "l, F j, Y g:i A",
"float": true,
"inherit_index": true, "inherit_index": true,
"repo": "dauxio/daux.io", "repo": "dauxio/daux.io",
"edit_on_github": "dauxio/daux.io/blob/master/docs", "edit_on_github": "dauxio/daux.io/blob/master/docs",
"twitter": ["onigoetz"], "twitter": ["onigoetz"],
"google_analytics": false, "google_analytics": "UA-3551397-7",
"plausible_domain": false,
"links": { "links": {
"GitHub Repository": "https://github.com/dauxio/daux.io", "Download": "https://github.com/dauxio/daux.io/archive/master.zip",
"Help/Support/Bugs": "https://github.com/dauxio/daux.io/issues", "GitHub Repo": "https://github.com/dauxio/daux.io",
"Packagist": "https://packagist.org/packages/daux/daux.io", "Help/Support/Bugs": "https://github.com/dauxio/daux.io/issues"
"Docker Images": "https://hub.docker.com/r/daux/daux.io"
}, },
"powered_by": "Powered by Daux.io" "powered_by": "Powered by Daux.io"
}, },
"confluence": { "confluence": {

View File

@ -8,8 +8,6 @@
"image": "", "image": "",
"languages": {}, "languages": {},
"cache": true,
"format": "html", "format": "html",
"processor": "", "processor": "",
@ -22,33 +20,26 @@
"strings": { "strings": {
"en": { "en": {
"CodeBlocks_title": "Code blocks",
"CodeBlocks_hide": "No",
"CodeBlocks_below": "Below",
"CodeBlocks_inline": "Inline",
"CodeBlocks_show": "Show Code Blocks", "CodeBlocks_show": "Show Code Blocks",
"Search_placeholder": "Search...", "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_previous": "Previous",
"Link_next": "Next", "Link_next": "Next",
"Edit_on": "Edit on :name:", "Edit_on": "Edit on :name:",
"View_on_github": "View On GitHub", "View_on_github": "View On GitHub",
"View_documentation": "View Documentation", "View_documentation": "View Documentation",
"Table_of_contents": "Table of Contents", "Table_of_contents": "Table of Contents"
"Toggle_navigation": "Toggle navigation"
}, },
"fr": { "fr": {
"CodeBlocks_title": "Afficher le code",
"CodeBlocks_hide": "Non",
"CodeBlocks_below": "En Dessous",
"CodeBlocks_inline": "A côté",
"CodeBlocks_show": "Afficher le code", "CodeBlocks_show": "Afficher le code",
"Search_placeholder": "Rechercher...", "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_previous": "Précédent",
"Link_next": "Suivant", "Link_next": "Suivant",
"Edit_on": "Editer sur :name:", "Edit_on": "Editer sur :name:",
@ -57,38 +48,18 @@
"Table_of_contents": "Table des matières" "Table_of_contents": "Table des matières"
}, },
"de": { "de": {
"CodeBlocks_title": "Code-Blöcke",
"CodeBlocks_hide": "Aus",
"CodeBlocks_below": "Unterhalb",
"CodeBlocks_inline": "Linear",
"CodeBlocks_show": "Code-Blöcke anzeigen", "CodeBlocks_show": "Code-Blöcke anzeigen",
"Search_placeholder": "Suchen...", "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_previous": "Zurück",
"Link_next": "Weiter", "Link_next": "Weiter",
"Edit_on": "Bearbeiten bei :name:", "Edit_on": "Bearbeiten bei :name:",
"View_on_github": "Bei GitHub anzeigen", "View_on_github": "Bei GitHub anzeigen",
"View_documentation": "Dokumentation anzeigen", "View_documentation": "Dokumentation anzeigen",
"Table_of_contents": "Inhaltsverzeichnis" "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"
} }
}, },
@ -104,6 +75,8 @@
"breadcrumb_separator": "Chevrons", "breadcrumb_separator": "Chevrons",
"toggle_code": true, "toggle_code": true,
"date_modified": false, "date_modified": false,
"date_modified_format": "l, F j, Y g:i A",
"float": false,
"auto_landing": true, "auto_landing": true,
"search": true, "search": true,
"auto_toc": false, "auto_toc": false,

View File

@ -5,7 +5,7 @@ use ArrayObject;
class BaseConfig extends ArrayObject class BaseConfig extends ArrayObject
{ {
/** /**
* Merge an array into the object. * Merge an array into the object
* *
* @param array $newValues * @param array $newValues
* @param bool $override * @param bool $override
@ -15,9 +15,8 @@ class BaseConfig extends ArrayObject
foreach ($newValues as $key => $value) { foreach ($newValues as $key => $value) {
// If the key doesn't exist yet, // If the key doesn't exist yet,
// we can simply set it. // we can simply set it.
if (!array_key_exists($key, (array) $this)) { if (!array_key_exists($key, $this)) {
$this[$key] = $value; $this[$key] = $value;
continue; 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;
}
} }

View File

@ -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);
}
}
}

View File

@ -1,116 +1,88 @@
<?php namespace Todaymade\Daux; <?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\Content;
use Todaymade\Daux\Tree\Entry; use Todaymade\Daux\Format\HTML\Config as HTMLConfig;
class Config extends BaseConfig class Config extends BaseConfig
{ {
public function getTitle() public function getTitle()
{ {
return $this->getValue('title'); return $this['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');
} }
public function getCurrentPage() public function getCurrentPage()
{ {
return $this->getValue('current_page'); return $this['current_page'];
} }
public function setCurrentPage(Content $entry) public function setCurrentPage(Content $entry)
{ {
$this->setValue('current_page', $entry); $this['current_page'] = $entry;
} }
public function getDocumentationDirectory() public function getDocumentationDirectory()
{ {
return $this->getValue('docs_directory'); return $this['docs_directory'];
}
public function setDocumentationDirectory($documentationPath)
{
$this['docs_directory'] = $documentationPath;
} }
public function getThemesDirectory() 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() public function getThemesPath()
{ {
return $this->getValue('themes_path'); return $this['themes_path'];
}
public function setFormat($format)
{
$this['format'] = $format;
} }
public function getFormat() public function getFormat()
{ {
return $this->getValue('format'); return $this['format'];
} }
public function hasTimezone(): bool public function hasTimezone()
{ {
return isset($this['timezone']); return isset($this['timezone']);
} }
public function getTimezone() public function getTimezone()
{ {
return $this->getValue('timezone'); return $this['timezone'];
} }
public function getTree() public function isMultilanguage()
{ {
return $this->getValue('tree'); return array_key_exists('languages', $this) && !empty($this['languages']);
}
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');
} }
public function isLive() public function isLive()
{ {
return $this->getValue('mode') == Daux::LIVE_MODE; return $this['mode'] == Daux::LIVE_MODE;
} }
public function isStatic() public function isStatic()
{ {
return $this->getValue('mode') == Daux::STATIC_MODE; return $this['mode'] == Daux::STATIC_MODE;
} }
public function shouldInheritIndex() public function shouldInheritIndex()
@ -118,188 +90,34 @@ class Config extends BaseConfig
// As the global configuration is always present, we // As the global configuration is always present, we
// need to test for the existence of the legacy value // need to test for the existence of the legacy value
// first. Then use the current 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['live']['inherit_index'];
} }
return $this['html']['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 new ConfluenceConfig($this->hasValue('confluence') ? $this->getValue('confluence') : []);
}
public function getHTML(): HTMLConfig
{
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; return null;
} }
public function getCacheKey() public function getConfluenceConfiguration()
{ {
$cloned = []; return $this['confluence'];
foreach ($this as $key => $value) {
$cloned[$key] = ($value instanceof Entry) ? $value->dump() : $value;
}
return sha1(json_encode($cloned));
} }
public function hasTranslationKey($language, $key): bool public function getHTML()
{ {
return array_key_exists($language, $this['strings']) && array_key_exists($key, $this['strings'][$language]); return new HTMLConfig($this['html']);
}
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');
} }
} }

View File

@ -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;
}
}

View File

@ -10,18 +10,17 @@ class Application extends SymfonyApplication
$this->add(new Generate()); $this->add(new Generate());
$this->add(new Serve()); $this->add(new Serve());
$this->add(new ClearCache());
$app_name = 'daux/daux.io'; $app_name = "daux/daux.io";
$up = '..' . DIRECTORY_SEPARATOR; $up = '..' . DIRECTORY_SEPARATOR;
$composer = __DIR__ . DIRECTORY_SEPARATOR . $up . $up . $up . $up . $up . 'composer.lock'; $composer = __DIR__ . DIRECTORY_SEPARATOR . $up . $up . $up . $up . $up . 'composer.lock';
$version = 'unknown'; $version = "unknown";
if (file_exists($composer)) { if (file_exists($composer)) {
$app = json_decode(file_get_contents($composer)); $app = json_decode(file_get_contents($composer));
$packages = $app->packages; $packages = $app->packages;
foreach ($packages as $package) { foreach ($packages as $package) {
if ($package->name == $app_name) { if ($package->name == $app_name) {
$version = $package->version; $version = $package->version;
@ -31,5 +30,6 @@ class Application extends SymfonyApplication
$this->setVersion($version); $this->setVersion($version);
$this->setName($app_name); $this->setName($app_name);
$this->setDefaultCommand('generate');
} }
} }

View File

@ -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;
}
}

View File

@ -4,64 +4,81 @@ use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\ConfigBuilder;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
class DauxCommand extends SymfonyCommand class DauxCommand extends SymfonyCommand
{ {
protected function configure() protected function configure()
{ {
$this $this
->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Configuration file') ->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'Configuration file')
->addOption('value', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Set different configuration values')
->addOption('source', 's', InputOption::VALUE_REQUIRED, 'Where to take the documentation from') ->addOption('source', 's', InputOption::VALUE_REQUIRED, 'Where to take the documentation from')
->addOption('processor', 'p', InputOption::VALUE_REQUIRED, 'Manipulations on the tree') ->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)') // HTML Format only
->addOption('value', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Set different configuration values'); $this->addOption('themes', 't', InputOption::VALUE_REQUIRED, 'Set a different themes directory');
} }
protected function prepareConfig($mode, InputInterface $input, OutputInterface $output): ConfigBuilder private function setValue(&$array, $key, $value)
{ {
$builder = ConfigBuilder::withMode($mode); 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;
return $array;
}
if ($input->getOption('configuration')) { private function applyConfiguration(array $options, Daux $daux)
$builder->withConfigurationOverride($input->getOption('configuration')); {
$values = array_map(
function($value) {
return array_map("trim", explode('=', $value));
},
$options
);
foreach ($values as $value) {
$this->setValue($daux->options, $value[0], $value[1]);
}
}
protected function prepareDaux(InputInterface $input, OutputInterface $output)
{
$daux = new Daux(Daux::STATIC_MODE, $output);
// 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')) { if ($input->getOption('source')) {
$builder->withDocumentationDirectory($input->getOption('source')); $daux->getParams()->setDocumentationDirectory($input->getOption('source'));
}
if ($input->getOption('processor')) {
$builder->withProcessor($input->getOption('processor'));
}
if ($input->getOption('no-cache')) {
$builder->withCache(false);
} }
if ($input->getOption('themes')) { if ($input->getOption('themes')) {
$builder->withThemesDirectory($input->getOption('themes')); $daux->getParams()->setThemesDirectory($input->getOption('themes'));
}
$daux->initializeConfiguration($input->getOption('configuration'));
if ($input->hasOption('delete') && $input->getOption('delete')) {
$daux->getParams()['confluence']['delete'] = true;
} }
if ($input->hasOption('value')) { if ($input->hasOption('value')) {
$values = array_map( $this->applyConfiguration($input->getOption('value'), $daux);
function ($value) {
return array_map('trim', explode('=', $value));
},
$input->getOption('value')
);
$builder->withValues($values);
} }
return $builder; return $daux;
}
protected function prepareProcessor(Daux $daux, $width)
{
$class = $daux->getProcessorClass();
if (!empty($class)) {
$daux->setProcessor(new $class($daux, $daux->getOutput(), $width));
}
} }
} }

View File

@ -6,7 +6,6 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal; use Symfony\Component\Console\Terminal;
use Todaymade\Daux\ConfigBuilder;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
class Generate extends DauxCommand class Generate extends DauxCommand
@ -26,23 +25,8 @@ class Generate extends DauxCommand
// Confluence format only // Confluence format only
->addOption('delete', null, InputOption::VALUE_NONE, 'Delete pages not linked to a documentation page (confluence)') ->addOption('delete', null, InputOption::VALUE_NONE, 'Delete pages not linked to a documentation page (confluence)')
->addOption('destination', 'd', InputOption::VALUE_REQUIRED, $description, 'static'); ->addOption('destination', 'd', InputOption::VALUE_REQUIRED, $description, 'static')
} ->addOption('search', null, InputOption::VALUE_NONE, 'Generate full text search');
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;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
@ -58,20 +42,29 @@ class Generate extends DauxCommand
$input = new ArgvInput($argv, $this->getDefinition()); $input = new ArgvInput($argv, $this->getDefinition());
} }
$builder = $this->prepareConfig(Daux::STATIC_MODE, $input, $output); $daux = $this->prepareDaux($input, $output);
$daux = new Daux($builder->build(), $output);
$width = (new Terminal())->getWidth(); $width = (new Terminal)->getWidth();
// Instiantiate the processor if one is defined // Instiantiate the processor if one is defined
$this->prepareProcessor($daux, $width); $this->prepareProcessor($daux, $input, $output, $width);
// Generate the tree // Generate the tree
$daux->generateTree(); $daux->generateTree();
// Generate the documentation // Generate the documentation
$daux->getGenerator()->generateAll($input, $output, $width); $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));
}
} }
} }

View File

@ -5,8 +5,7 @@ use Todaymade\Daux\Daux;
trait RunAction trait RunAction
{ {
protected function getLength($content) protected function getLength($content) {
{
return function_exists('mb_strlen') ? mb_strlen($content) : strlen($content); return function_exists('mb_strlen') ? mb_strlen($content) : strlen($content);
} }
@ -20,13 +19,12 @@ trait RunAction
$padding = $width - $this->getLength($title) - 10; $padding = $width - $this->getLength($title) - 10;
try { try {
$response = $closure(function ($content) use (&$padding, $verbose) { $response = $closure(function ($content) use (&$padding) {
$padding -= $this->getLength($content); $padding -= $this->getLength($content);
Daux::write($content, $verbose); Daux::write($content, $verbose);
}); });
} catch (\Exception $e) { } catch (\Exception $e) {
$this->status($padding, '[ <fg=red>FAIL</fg=red> ]'); $this->status($padding, '[ <fg=red>FAIL</fg=red> ]');
throw $e; throw $e;
} }
$this->status($padding, '[ <fg=green>OK</fg=green> ]'); $this->status($padding, '[ <fg=green>OK</fg=green> ]');

View File

@ -1,11 +1,13 @@
<?php namespace Todaymade\Daux\Console; <?php namespace Todaymade\Daux\Console;
use Exception;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\ProcessUtils;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
use Todaymade\Daux\Server\Server;
class Serve extends DauxCommand class Serve extends DauxCommand
{ {
@ -17,6 +19,8 @@ class Serve extends DauxCommand
->setName('serve') ->setName('serve')
->setDescription('Serve documentation') ->setDescription('Serve documentation')
// 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('host', null, InputOption::VALUE_REQUIRED, 'The host to serve on', 'localhost')
->addOption('port', null, InputOption::VALUE_REQUIRED, 'The port to serve on', 8085); ->addOption('port', null, InputOption::VALUE_REQUIRED, 'The port to serve on', 8085);
} }
@ -26,44 +30,31 @@ class Serve extends DauxCommand
$host = $input->getOption('host'); $host = $input->getOption('host');
$port = $input->getOption('port'); $port = $input->getOption('port');
$builder = $this->prepareConfig(Daux::LIVE_MODE, $input, $output); $daux = $this->prepareDaux($input, $output);
// Daux can only serve HTML // Daux can only serve HTML
$builder->withFormat('html'); $daux->getParams()->setFormat('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()));
chdir(__DIR__ . '/../../'); chdir(__DIR__ . '/../../');
putenv('DAUX_CONFIG=' . $path); putenv('DAUX_SOURCE=' . $daux->getParams()->getDocumentationDirectory());
putenv('DAUX_VERBOSITY=' . $output->getVerbosity()); putenv('DAUX_THEME=' . $daux->getParams()->getThemesPath());
putenv('DAUX_CONFIGURATION=' . $daux->getParams()->getConfigurationOverrideFile());
putenv('DAUX_EXTENSION=' . DAUX_EXTENSION); putenv('DAUX_EXTENSION=' . DAUX_EXTENSION);
$base = escapeshellarg(__DIR__ . '/../../'); $base = ProcessUtils::escapeArgument(__DIR__ . '/../../');
$binary = escapeshellarg((new PhpExecutableFinder())->find(false)); $binary = ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
echo "Daux development server started on http://{$host}:{$port}/\n"; echo "Daux development server started on http://{$host}:{$port}/\n";
passthru("{$binary} -S {$host}:{$port} {$base}/index.php"); if (defined('HHVM_VERSION')) {
fclose($file); 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");
return 0; } else {
throw new Exception("HHVM's built-in server requires HHVM >= 3.8.0.");
}
} else {
passthru("{$binary} -S {$host}:{$port} {$base}/index.php");
}
} }
} }

View File

@ -8,7 +8,7 @@ interface ContentType
public function __construct(Config $config); 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[] * @return string[]
*/ */
@ -17,7 +17,6 @@ interface ContentType
/** /**
* @param string $raw The raw text to render * @param string $raw The raw text to render
* @param Content $node The original node we are converting * @param Content $node The original node we are converting
*
* @return string The generated output * @return string The generated output
*/ */
public function convert($raw, Content $node); public function convert($raw, Content $node);

View File

@ -18,7 +18,7 @@ class ContentTypeHandler
} }
/** /**
* Get all valid content file extensions. * Get all valid content file extensions
* *
* @return string[] * @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 * @return ContentType
*/ */
public function getType(Content $node) public function getType(Content $node)

View File

@ -1,25 +1,22 @@
<?php namespace Todaymade\Daux\ContentTypes\Markdown; <?php namespace Todaymade\Daux\ContentTypes\Markdown;
use League\CommonMark\DocParser;
use League\CommonMark\Environment; use League\CommonMark\Environment;
use League\CommonMark\Extension\Autolink\AutolinkExtension; use League\CommonMark\HtmlRenderer;
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 Todaymade\Daux\Config; use Todaymade\Daux\Config;
use Webuni\CommonMark\TableExtension\TableExtension;
class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter
{ {
/** /**
* Create a new commonmark converter instance. * Create a new commonmark converter instance.
*
* @param array $config
*/ */
public function __construct(array $config = []) public function __construct(array $config = [])
{ {
$environment = Environment::createCommonMarkEnvironment(); $environment = Environment::createCommonMarkEnvironment();
$environment->mergeConfig($config); $environment->mergeConfig($config);
$environment->addExtension(new AutolinkExtension());
$environment->addExtension(new SmartPunctExtension());
$environment->addExtension(new StrikethroughExtension());
$environment->addExtension(new TableExtension()); $environment->addExtension(new TableExtension());
// Table of Contents // Table of Contents
@ -27,11 +24,12 @@ class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter
$this->extendEnvironment($environment, $config['daux']); $this->extendEnvironment($environment, $config['daux']);
if ($config['daux']->hasProcessorInstance()) { if (array_key_exists('processor_instance', $config['daux'])) {
$config['daux']->getProcessorInstance()->extendCommonMarkEnvironment($environment); $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) protected function getLinkRenderer(Environment $environment)
@ -41,6 +39,6 @@ class CommonMarkConverter extends \League\CommonMark\CommonMarkConverter
protected function extendEnvironment(Environment $environment, Config $config) protected function extendEnvironment(Environment $environment, Config $config)
{ {
$environment->addInlineRenderer(InlineElement\Link::class, $this->getLinkRenderer($environment)); $environment->addInlineRenderer('Link', $this->getLinkRenderer($environment));
} }
} }

View File

@ -1,9 +1,6 @@
<?php namespace Todaymade\Daux\ContentTypes\Markdown; <?php namespace Todaymade\Daux\ContentTypes\Markdown;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Cache;
use Todaymade\Daux\Config; use Todaymade\Daux\Config;
use Todaymade\Daux\Daux;
use Todaymade\Daux\Tree\Content; use Todaymade\Daux\Tree\Content;
class ContentType implements \Todaymade\Daux\ContentTypes\ContentType class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
@ -12,25 +9,12 @@ class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
protected $config; protected $config;
/** @var CommonMarkConverter */ /** @var CommonMarkConverter */
private $converter; protected $converter;
public function __construct(Config $config) public function __construct(Config $config)
{ {
$this->config = $config; $this->config = $config;
} $this->converter = new CommonMarkConverter(['daux' => $config]);
protected function createConverter()
{
return new CommonMarkConverter(['daux' => $this->config]);
}
protected function getConverter()
{
if (!$this->converter) {
$this->converter = $this->createConverter();
}
return $this->converter;
} }
/** /**
@ -41,37 +25,10 @@ class ContentType implements \Todaymade\Daux\ContentTypes\ContentType
return ['md', 'markdown']; 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) public function convert($raw, Content $node)
{ {
$this->config->setCurrentPage($node); $this->config->setCurrentPage($node);
$can_cache = $this->config->canCache(); return $this->converter->convertToHtml($raw);
// 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;
} }
} }

View File

@ -4,58 +4,102 @@ use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement; use League\CommonMark\HtmlElement;
use League\CommonMark\Inline\Element\AbstractInline; use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Link; 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\Config;
use Todaymade\Daux\DauxHelper; use Todaymade\Daux\DauxHelper;
use Todaymade\Daux\Exception\LinkNotFoundException; 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 * @var Config
*/ */
protected $daux; protected $daux;
/**
* @var \League\CommonMark\Inline\Renderer\LinkRenderer
*/
protected $parent;
public function __construct($daux) public function __construct($daux)
{ {
$this->daux = $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 * @param AbstractInline|Link $inline
* * @param ElementRendererInterface $htmlRenderer
* @throws LinkNotFoundException
*
* @return HtmlElement * @return HtmlElement
* @throws LinkNotFoundException
*/ */
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer) public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
{ {
if (!($inline instanceof Link)) { // This can't be in the method type as
throw new \InvalidArgumentException('Incompatible inline type: ' . \get_class($inline)); // 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(); $url = $inline->getUrl();
// empty urls and anchors should // empty urls and anchors should
// not go through the url resolver // not go through the url resolver
if (!DauxHelper::isValidUrl($url)) { if (!$this->isValidUrl($url)) {
return $element; return $element;
} }
// Absolute urls, shouldn't either // Absolute urls, shouldn't either
if (DauxHelper::isExternalUrl($url)) { if ($this->isExternalUrl($url)) {
$element->setAttribute('class', 'Link--external'); $element->setAttribute('class', 'Link--external');
$element->setAttribute('rel', 'noopener noreferrer');
return $element; return $element;
} }
@ -65,35 +109,18 @@ class LinkRenderer implements InlineRendererInterface, ConfigurationAwareInterfa
$urlAndHash = explode('#', $url); $urlAndHash = explode('#', $url);
$url = $urlAndHash[0]; $url = $urlAndHash[0];
$foundWithHash = false;
try { try {
$file = DauxHelper::resolveInternalFile($this->daux, $url); $file = $this->resolveInternalFile($url);
$url = DauxHelper::getRelativePath($this->daux->getCurrentPage()->getUrl(), $file->getUrl()); $url = DauxHelper::getRelativePath($this->daux->getCurrentPage()->getUrl(), $file->getUrl());
} catch (LinkNotFoundException $e) { } catch (LinkNotFoundException $e) {
// For some reason, the filename could contain a # and thus the link needs to resolve to that. if ($this->daux->isStatic()) {
try { throw $e;
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) { $element->setAttribute('class', 'Link--broken');
if ($this->daux->isStatic()) {
throw $e;
}
$element->setAttribute('class', 'Link--broken');
}
} }
if (!$foundWithHash && isset($urlAndHash[1])) { if (isset($urlAndHash[1])) {
$url .= '#' . $urlAndHash[1]; $url .= '#' . $urlAndHash[1];
} }
@ -101,9 +128,4 @@ class LinkRenderer implements InlineRendererInterface, ConfigurationAwareInterfa
return $element; return $element;
} }
public function setConfiguration(ConfigurationInterface $configuration)
{
$this->parent->setConfiguration($configuration);
}
} }

View File

@ -6,22 +6,43 @@ use League\CommonMark\Cursor;
class TableOfContents extends AbstractBlock 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; 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; 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; return false;
} }

View File

@ -1,19 +1,25 @@
<?php namespace Todaymade\Daux\ContentTypes\Markdown; <?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\ContextInterface;
use League\CommonMark\Cursor; 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()) { if ($cursor->isIndented()) {
return false; return false;
} }
$previousState = $cursor->saveState(); $previousState = $cursor->saveState();
$cursor->advanceToNextNonSpaceOrNewline(); $cursor->advanceToFirstNonSpace();
$fence = $cursor->match('/^\[TOC\]/'); $fence = $cursor->match('/^\[TOC\]/');
if (is_null($fence)) { if (is_null($fence)) {
$cursor->restoreState($previousState); $cursor->restoreState($previousState);

View File

@ -4,6 +4,8 @@ use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\ContentTypes\ContentTypeHandler; use Todaymade\Daux\ContentTypes\ContentTypeHandler;
use Todaymade\Daux\Tree\Builder; use Todaymade\Daux\Tree\Builder;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
use Todaymade\Daux\Tree\Root; use Todaymade\Daux\Tree\Root;
class Daux class Daux
@ -13,11 +15,8 @@ class Daux
public static $output; public static $output;
/** @var Tree\Root */ /** @var string */
public $tree; public $local_base;
/** @var Config */
public $config;
/** @var \Todaymade\Daux\Format\Base\Generator */ /** @var \Todaymade\Daux\Format\Base\Generator */
protected $generator; protected $generator;
@ -25,35 +24,182 @@ class Daux
/** @var ContentTypeHandler */ /** @var ContentTypeHandler */
protected $typeHandler; protected $typeHandler;
/** @var string[] */ /**
* @var string[]
*/
protected $validExtensions; protected $validExtensions;
/** @var Processor */ /** @var Processor */
protected $processor; protected $processor;
/** @var Tree\Root */
public $tree;
/** @var Config */
public $options;
/** @var string */
private $mode;
/** @var bool */ /** @var bool */
private $merged_tree = false; private $merged_tree = false;
public function __construct(Config $config, OutputInterface $output) /**
* @param string $mode
*/
public function __construct($mode, OutputInterface $output)
{ {
Daux::$output = $output; Daux::$output = $output;
$this->mode = $mode;
$this->config = $config; $this->local_base = dirname(__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 $path
* @return string|null the path to a file to load for configuration overrides
* @throws Exception
*/
public function getConfigurationOverride($path)
{
$validPath = DauxHelper::findLocation($path, $this->local_base, 'DAUX_CONFIGURATION', '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;
}
public function normalizeThemePath($path)
{
$validPath = DauxHelper::findLocation($path, $this->local_base, 'DAUX_THEME', 'dir');
if (!$validPath) {
throw new Exception('The Themes directory does not exist. Check the path again : ' . $path);
}
return $validPath;
}
public function normalizeDocumentationPath($path)
{
$validPath = DauxHelper::findLocation($path, $this->local_base, 'DAUX_SOURCE', '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
*/
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() public function generateTree()
{ {
$this->config->setValidContentExtensions($this->getContentExtensions()); $this->options['valid_content_extensions'] = $this->getContentExtensions();
$this->tree = new Root($this->getConfig()); $this->tree = new Root($this->getParams());
Builder::build($this->tree, $this->config->getIgnore()); Builder::build($this->tree, $this->options['ignore']);
// Apply the language name as Section title // Apply the language name as Section title
if ($this->config->isMultilanguage()) { if ($this->options->isMultilanguage()) {
foreach ($this->config->getLanguages() as $key => $node) { foreach ($this->options['languages'] as $key => $node) {
$this->tree->getEntries()[$key]->setTitle($node); $this->tree->getEntries()[$key]->setTitle($node);
} }
} }
@ -70,35 +216,22 @@ class Daux
/** /**
* @return Config * @return Config
*/ */
public function getConfig() public function getParams()
{ {
if ($this->tree && !$this->merged_tree) { if ($this->tree && !$this->merged_tree) {
$this->config->setTree($this->tree); $this->options['tree'] = $this->tree;
$this->config->setIndex($this->tree->getIndexPage() ?: $this->tree->getFirstPage()); $this->options['index'] = $this->tree->getIndexPage() ?: $this->tree->getFirstPage();
$entry_page = null; if ($this->options->isMultilanguage()) {
if ($this->config->isMultilanguage()) { foreach ($this->options['languages'] as $key => $name) {
$entry_page = []; $this->options['entry_page'][$key] = $this->tree->getEntries()[$key]->getFirstPage();
foreach ($this->config->getLanguages() as $key => $name) {
$entry_page[$key] = $this->tree->getEntries()[$key]->getFirstPage();
} }
} else { } else {
$entry_page = $this->tree->getFirstPage(); $this->options['entry_page'] = $this->tree->getFirstPage();
} }
$this->config->setEntryPage($entry_page);
$this->merged_tree = true; $this->merged_tree = true;
} }
return $this->config; return $this->options;
}
/**
* @return Config
*
* @deprecated Use getConfig instead
*/
public function getParams()
{
return $this->getConfig();
} }
/** /**
@ -113,14 +246,17 @@ class Daux
return $this->processor; return $this->processor;
} }
/**
* @param Processor $processor
*/
public function setProcessor(Processor $processor) public function setProcessor(Processor $processor)
{ {
$this->processor = $processor; $this->processor = $processor;
// This is not the cleanest but it's // 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 // processor in very remote places
$this->config->setProcessorInstance($processor); $this->options['processor_instance'] = $processor;
} }
public function getGenerators() public function getGenerators()
@ -136,9 +272,10 @@ class Daux
return array_replace($default, $extended); return array_replace($default, $extended);
} }
public function getProcessorClass() public function getProcessorClass()
{ {
$processor = $this->getConfig()->getProcessor(); $processor = $this->getParams()['processor'];
if (empty($processor)) { if (empty($processor)) {
return null; return null;
@ -156,21 +293,6 @@ class Daux
return $class; 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 * @return \Todaymade\Daux\Format\Base\Generator
*/ */
@ -182,22 +304,10 @@ class Daux
$generators = $this->getGenerators(); $generators = $this->getGenerators();
$format = $this->getConfig()->getFormat(); $format = $this->getParams()->getFormat();
if (!array_key_exists($format, $generators)) { if (!array_key_exists($format, $generators)) {
$message = "The format '$format' doesn't exist, did you forget to set your processor ?"; throw new \RuntimeException("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);
} }
$class = $generators[$format]; $class = $generators[$format];
@ -229,7 +339,7 @@ class Daux
} }
/** /**
* Get all content file extensions. * Get all content file extensions
* *
* @return string[] * @return string[]
*/ */
@ -242,10 +352,9 @@ class Daux
return $this->validExtensions = $this->getContentTypeHandler()->getContentExtensions(); return $this->validExtensions = $this->getContentTypeHandler()->getContentExtensions();
} }
public static function getOutput() public static function getOutput() {
{
if (!Daux::$output) { if (!Daux::$output) {
Daux::$output = new NullOutput(); Daux:$output = new NullOutput();
} }
return Daux::$output; return Daux::$output;
@ -254,28 +363,25 @@ class Daux
/** /**
* Writes a message to the output. * Writes a message to the output.
* *
* @param array|string $messages The message as an array of lines or a single string * @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline * @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 * @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) public static function write($messages, $newline = false, $options = 0) {
{ Daux::$output->write($messages, $newline, $options);
Daux::getOutput()->write($messages, $newline, $options);
} }
/** /**
* Writes a message to the output and adds a newline at the end. * 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 string|array $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 * @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) public static function writeln($messages, $options = 0) {
{ Daux::write($messages, true, $options);
Daux::getOutput()->write($messages, true, $options);
} }
public static function getVerbosity() public static function getVerbosity() {
{
return Daux::getOutput()->getVerbosity(); return Daux::getOutput()->getVerbosity();
} }
} }

View File

@ -1,48 +1,65 @@
<?php namespace Todaymade\Daux; <?php namespace Todaymade\Daux;
use Todaymade\Daux\Exception\LinkNotFoundException;
use Todaymade\Daux\Tree\Builder; use Todaymade\Daux\Tree\Builder;
use Todaymade\Daux\Tree\Directory; use Todaymade\Daux\Tree\Directory;
use Todaymade\Daux\Tree\Entry;
class DauxHelper 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 * @param string $base_url
*/ */
public static function rebaseConfiguration(Config $config, $base_url) public static function rebaseConfiguration(Config $config, $base_url)
{ {
// Avoid changing the url if it is already correct // 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; return;
} }
// Change base url for all links on the pages // 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['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 * @param string $current_url
*
* @return array * @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 = $params->getThemesPath() . DIRECTORY_SEPARATOR . $params['html']['theme'];
$theme_url = $params['base_url'] . 'themes/' . $params['html']['theme'] . '/';
$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 = []; $theme = [];
if (is_file($theme_folder . DIRECTORY_SEPARATOR . 'config.json')) { if (is_file($theme_folder . DIRECTORY_SEPARATOR . 'config.json')) {
@ -52,20 +69,19 @@ class DauxHelper
} }
} }
// Default parameters for theme //Default parameters for theme
$theme += [ $theme += [
'name' => $htmlTheme, 'name' => $params['html']['theme'],
'css' => [], 'css' => [],
'js' => [], 'js' => [],
'fonts' => [], 'fonts' => [],
'favicon' => '<base_url>themes/daux/img/favicon.png', 'favicon' => '<base_url>themes/daux/img/favicon.png',
'templates' => $theme_folder . DIRECTORY_SEPARATOR . 'templates', 'templates' => $theme_folder . DIRECTORY_SEPARATOR . 'templates',
'variants' => [], 'variants' => [],
'with_search' => $config->getHTML()->hasSearch()
]; ];
if ($config->getHTML()->hasThemeVariant()) { if (array_key_exists('theme-variant', $params['html'])) {
$variant = $config->getHTML()->getThemeVariant(); $variant = $params['html']['theme-variant'];
if (!array_key_exists($variant, $theme['variants'])) { if (!array_key_exists($variant, $theme['variants'])) {
throw new Exception("Variant '$variant' not found for theme '$theme[name]'"); 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 = [ $substitutions = [
'<local_base>' => $config->getLocalBase(), '<local_base>' => $params['local_base'],
'<base_url>' => $current_url, '<base_url>' => $current_url,
'<theme_url>' => $theme_url, '<theme_url>' => $theme_url,
]; ];
@ -109,16 +117,13 @@ class DauxHelper
} }
} }
$cache[$cache_key] = $theme;
return $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 * @param string $path
*
* @return string * @return string
*/ */
public static function getCleanPath($path) public static function getCleanPath($path)
@ -143,13 +148,13 @@ class DauxHelper
/** /**
* Get the possible output file names for a source file. * Get the possible output file names for a source file.
* *
* @param Config $config
* @param string $part * @param string $part
*
* @return string[] * @return string[]
*/ */
public static function getFilenames(Config $config, $part) 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 = preg_replace('/(.*)?\\.(' . $extensions . ')$/', '$1', $part);
$raw = Builder::removeSortingInformations($raw); $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 Directory $tree
* @param string $request * @param string $request
* * @return Tree\Content|Tree\Raw|false
* @return false|Tree\Content|Tree\Raw
*/ */
public static function getFile($tree, $request) public static function getFile($tree, $request)
{ {
@ -182,24 +186,16 @@ class DauxHelper
if ($node == '..') { if ($node == '..') {
$tree = $tree->getParent(); $tree = $tree->getParent();
continue; continue;
} }
$node = urldecode($node);
// if the node exists in the current request tree, // if the node exists in the current request tree,
// change the $tree variable to reference the new // change the $tree variable to reference the new
// node and proceed to the next url part // node and proceed to the next url part
if (isset($tree->getEntries()[$node])) { if (isset($tree->getEntries()[$node])) {
$tree = $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; continue;
} }
@ -209,7 +205,6 @@ class DauxHelper
foreach (static::getFilenames($tree->getConfig(), $node) as $filename) { foreach (static::getFilenames($tree->getConfig(), $node) as $filename) {
if (isset($tree->getEntries()[$filename])) { if (isset($tree->getEntries()[$filename])) {
$tree = $tree->getEntries()[$filename]; $tree = $tree->getEntries()[$filename];
continue 2; continue 2;
} }
} }
@ -242,28 +237,22 @@ class DauxHelper
* Taken from Stringy * Taken from Stringy
* *
* @param string $title * @param string $title
*
* @return string * @return string
*/ */
public static function slug($title) public static function slug($title)
{ {
// Convert to ASCII foreach (static::charsArray() as $key => $value) {
if (function_exists('transliterator_transliterate')) { $title = str_replace($value, $key, $title);
$title = transliterator_transliterate('Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC;', $title);
} }
$title = iconv('utf-8', 'ASCII//TRANSLIT//IGNORE', $title);
// Remove unsupported characters
$title = preg_replace('/[^\x20-\x7E]/u', '', $title); $title = preg_replace('/[^\x20-\x7E]/u', '', $title);
$separator = '_'; $separator = '_';
// Convert all dashes into underscores // Convert all dashes into underscores
$title = preg_replace('![' . preg_quote('-') . ']+!u', $separator, $title); $title = preg_replace('![' . preg_quote('-') . ']+!u', $separator, $title);
// Remove all characters that are not valid in a URL: // Remove all characters that are not the separator, letters, numbers, or whitespace.
// $-_.+!*'(), separator, letters, numbers, or whitespace. $title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', $title);
$title = preg_replace('![^-' . preg_quote($separator) . '\!\'\(\),\.\+\*\$\pL\pN\s]+!u', '', $title);
// Replace all separator characters and whitespace by a single separator // Replace all separator characters and whitespace by a single separator
$title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title); $title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
@ -271,10 +260,149 @@ class DauxHelper
return trim($title, $separator); 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 $from
* @param string $to * @param string $to
*
* @return string * @return string
*/ */
public static function getRelativePath($from, $to) public static function getRelativePath($from, $to)
@ -301,10 +429,10 @@ class DauxHelper
// add traversals up to first matching dir // add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1; $padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..'); $relPath = array_pad($relPath, $padLength, '..');
break; break;
} else {
//$relPath[0] = './' . $relPath[0];
} }
//$relPath[0] = './' . $relPath[0];
} }
} }
@ -315,13 +443,11 @@ class DauxHelper
{ {
if (!is_string($path)) { if (!is_string($path)) {
$mess = sprintf('String expected but was given %s', gettype($path)); $mess = sprintf('String expected but was given %s', gettype($path));
throw new \InvalidArgumentException($mess); throw new \InvalidArgumentException($mess);
} }
if (!ctype_print($path)) { if (!ctype_print($path)) {
$mess = 'Path can NOT have non-printable characters or be empty'; $mess = 'Path can NOT have non-printable characters or be empty';
throw new \DomainException($mess); throw new \DomainException($mess);
} }
@ -337,15 +463,13 @@ class DauxHelper
$parts = []; $parts = [];
if (!preg_match($regExp, $path, $parts)) { if (!preg_match($regExp, $path, $parts)) {
$mess = sprintf('Path is NOT valid, was given %s', $path); $mess = sprintf('Path is NOT valid, was given %s', $path);
throw new \DomainException($mess); throw new \DomainException($mess);
} }
return '' !== $parts['root']; return '' !== $parts['root'];
} }
public static function getAbsolutePath($path) public static function getAbsolutePath($path) {
{
if (DauxHelper::isAbsolutePath($path)) { if (DauxHelper::isAbsolutePath($path)) {
return $path; return $path;
} }
@ -353,58 +477,40 @@ class DauxHelper
return getcwd() . '/' . $path; return getcwd() . '/' . $path;
} }
public static function is($path, $type) /**
{ * @param string|null $path
* @param string $basedir
* @param string $var The constant name to check
* @param "dir"|"file" $type
* @return false|null|string
*/
public static function findLocation($path, $basedir, $var, $type) {
// When running through `daux --serve` we set an environment variable to know where we started from
$env = getenv($var);
if ($env && DauxHelper::is($env, $type)) {
return $env;
}
// If Path is explicitly null, it's useless to go further
if ($path == null) {
return null;
}
// 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;
}
public static function is($path, $type) {
return ($type == 'dir') ? is_dir($path) : file_exists($path); 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);
}
} }

View File

@ -14,7 +14,7 @@ abstract class ContentPage extends SimplePage
/** /**
* @var Config * @var Config
*/ */
protected $config; protected $params;
/** /**
* @var ContentType * @var ContentType
@ -38,17 +38,9 @@ abstract class ContentPage extends SimplePage
return $this->file; return $this->file;
} }
public function setConfig(Config $config) public function setParams(Config $params)
{ {
$this->config = $config; $this->params = $params;
}
/**
* @deprecated use setConfig instead
*/
public function setParams(Config $config)
{
$this->setConfig($config);
} }
/** /**
@ -73,11 +65,11 @@ abstract class ContentPage extends SimplePage
return $this->getPureContent(); 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 = new static($file->getTitle(), $file->getContent());
$page->setFile($file); $page->setFile($file);
$page->setConfig($config); $page->setParams($params);
$page->setContentType($contentType); $page->setContentType($contentType);
return $page; return $page;

View File

@ -3,7 +3,7 @@
* Created by IntelliJ IDEA. * Created by IntelliJ IDEA.
* User: onigoetz * User: onigoetz
* Date: 06/11/15 * Date: 06/11/15
* Time: 20:27. * Time: 20:27
*/ */
namespace Todaymade\Daux\Format\Base; namespace Todaymade\Daux\Format\Base;
@ -25,7 +25,7 @@ class EmbedImages
{ {
return preg_replace_callback( return preg_replace_callback(
"/<img\\s+[^>]*src=['\"]([^\"]*)['\"][^>]*>/", "/<img\\s+[^>]*src=['\"]([^\"]*)['\"][^>]*>/",
function ($matches) use ($file, $callback) { function($matches) use ($file, $callback) {
if ($result = $this->findImage($matches[1], $matches[0], $file, $callback)) { if ($result = $this->findImage($matches[1], $matches[0], $file, $callback)) {
return $result; return $result;
} }
@ -67,6 +67,7 @@ class EmbedImages
//Get any file corresponding to the right one //Get any file corresponding to the right one
$file = DauxHelper::getFile($this->tree, $url); $file = DauxHelper::getFile($this->tree, $url);
if ($file === false) { if ($file === false) {
return false; return false;
} }

View File

@ -6,11 +6,15 @@ use Todaymade\Daux\Daux;
interface Generator interface Generator
{ {
/**
* @param Daux $daux
*/
public function __construct(Daux $daux); public function __construct(Daux $daux);
/** /**
* @param InputInterface $input
* @param OutputInterface $output
* @param int $width * @param int $width
*
* @return mixed * @return mixed
*/ */
public function generateAll(InputInterface $input, OutputInterface $output, $width); public function generateAll(InputInterface $input, OutputInterface $output, $width);

View File

@ -6,7 +6,9 @@ use Todaymade\Daux\Tree\Entry;
interface LiveGenerator extends Generator interface LiveGenerator extends Generator
{ {
/** /**
* @param Entry $node
* @param Config $params
* @return \Todaymade\Daux\Format\Base\Page * @return \Todaymade\Daux\Format\Base\Page
*/ */
public function generateOne(Entry $node, Config $config); public function generateOne(Entry $node, Config $params);
} }

View File

@ -3,14 +3,14 @@
interface Page interface Page
{ {
/** /**
* Get the converted content, without any template. * Get the converted content, without any template
* *
* @return string * @return string
*/ */
public function getPureContent(); public function getPureContent();
/** /**
* Get the full content. * Get the full content
* *
* @return mixed * @return mixed
*/ */

View File

@ -24,8 +24,7 @@ class Api
} }
/** /**
* This method is public due to test purposes. * This method is public due to test purposes
*
* @return Client * @return Client
*/ */
public function getClient() public function getClient()
@ -40,8 +39,9 @@ class Api
/** /**
* The standard error message from guzzle is quite poor in informations, * 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 * @return \Exception
*/ */
protected function handleError(BadResponseException $e) protected function handleError(BadResponseException $e)
@ -101,11 +101,10 @@ class Api
} }
/** /**
* Get a list of pages. * Get a list of pages
* *
* @param int $rootPage * @param int $rootPage
* @param bool $recursive * @param bool $recursive
*
* @return array * @return array
*/ */
public function getList($rootPage, $recursive = false) public function getList($rootPage, $recursive = false)
@ -154,7 +153,6 @@ class Api
* @param int $parent_id * @param int $parent_id
* @param string $title * @param string $title
* @param string $content * @param string $content
*
* @return int * @return int
*/ */
public function createPage($parent_id, $title, $content) public function createPage($parent_id, $title, $content)
@ -220,13 +218,8 @@ class Api
public function showSourceCode($css, $lineNumber, $column) public function showSourceCode($css, $lineNumber, $column)
{ {
$lines = preg_split("/\r?\n/", $css); $lines = preg_split("/\r?\n/", $css);
if ($lines === false) {
return $css;
}
$start = max($lineNumber - 3, 0); $start = max($lineNumber - 3, 0);
$end = min($lineNumber + 2, count($lines)); $end = min($lineNumber + 2, count($lines));
$maxWidth = strlen("$end"); $maxWidth = strlen("$end");
@ -234,11 +227,12 @@ class Api
$prepared = []; $prepared = [];
foreach ($filtered as $index => $line) { foreach ($filtered as $index => $line) {
$number = $start + 1 + $index; $number = $start + 1 + $index;
$gutter = substr(' ' . (' ' . $number), -$maxWidth) . ' | '; $gutter = substr(' ' . (' ' . $number), -$maxWidth) . ' | ';
if ($number == $lineNumber) { if ($number == $lineNumber) {
$spacing = str_repeat(' ', strlen($gutter) + $column - 2); $spacing = str_repeat(" ", strlen($gutter) + $column - 2);
$prepared[] = '>' . $gutter . $line . "\n " . $spacing . '^'; $prepared[] = '>' . $gutter . $line . "\n " . $spacing . '^';
} else { } else {
$prepared[] = ' ' . $gutter . $line; $prepared[] = ' ' . $gutter . $line;
@ -249,10 +243,9 @@ class Api
} }
/** /**
* Delete a page. * Delete a page
* *
* @param int $page_id * @param int $page_id
*
* @return mixed * @return mixed
*/ */
public function deletePage($page_id) public function deletePage($page_id)
@ -270,7 +263,6 @@ class Api
// this name is uploaded // this name is uploaded
try { try {
$url = "content/$id/child/attachment?filename=" . urlencode($attachment['filename']); $url = "content/$id/child/attachment?filename=" . urlencode($attachment['filename']);
return json_decode($this->getClient()->get($url)->getBody(), true); return json_decode($this->getClient()->get($url)->getBody(), true);
} catch (BadResponseException $e) { } catch (BadResponseException $e) {
throw $this->handleError($e); throw $this->handleError($e);
@ -321,9 +313,9 @@ class Api
// If the attachment is already uploaded, // If the attachment is already uploaded,
// the update URL is different // the update URL is different
if (count($result['results'])) { if (count($result['results'])) {
if ($this->getFileSize($attachment) == $result['results'][0]['extensions']['fileSize']) {
$write(' ( An attachment of the same size already exists, skipping. )');
if ($this->getFileSize($attachment) == $result['results'][0]['extensions']['fileSize']) {
$write(" ( An attachment of the same size already exists, skipping. )");
return; return;
} }

View File

@ -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');
}
}

View File

@ -15,16 +15,17 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
//Embed images //Embed images
// We do it after generation so we can catch the images that were in html already // 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( ->embed(
$content, $content,
$this->file, $this->file,
function ($src, array $attributes, Entry $file) { function($src, array $attributes, Entry $file) {
//Add the attachment for later upload //Add the attachment for later upload
if ($file instanceof Raw) { if ($file instanceof Raw) {
$filename = basename($file->getPath()); $filename = basename($file->getPath());
$this->attachments[$filename] = ['filename' => $filename, 'file' => $file]; $this->attachments[$filename] = ['filename' => $filename, 'file' => $file];
} elseif ($file instanceof ComputedRaw) { } else if ($file instanceof ComputedRaw) {
$filename = $file->getUri(); $filename = $file->getUri();
$this->attachments[$filename] = ['filename' => $filename, 'content' => $file->getContent()]; $this->attachments[$filename] = ['filename' => $filename, 'content' => $file->getContent()];
} else { } else {
@ -35,20 +36,20 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
} }
); );
$intro = ''; $intro = '';
if ($this->config->getConfluenceConfiguration()->hasHeader()) { 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->config->getConfluenceConfiguration()->getHeader() . '</ac:rich-text-body></ac:structured-macro>'; $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; return $intro . $content;
} }
/** /**
* Create an image tag for the specified filename. * Create an image tag for the specified filename
* *
* @param string $filename * @param string $filename
* @param array $attributes * @param array $attributes
*
* @return string * @return string
*/ */
private function createImageTag($filename, $attributes) private function createImageTag($filename, $attributes)
@ -60,7 +61,7 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
$re = '/float:\s*?(left|right);?/'; $re = '/float:\s*?(left|right);?/';
if (preg_match($re, $value, $matches)) { if (preg_match($re, $value, $matches)) {
$img .= ' ac:align="' . $matches[1] . '"'; $img .= ' ac:align="' . $matches[1] . '"';
$value = preg_replace($re, '', $value, 1); $value = preg_replace($re, "", $value, 1);
} }
} }

View File

@ -5,9 +5,10 @@ use League\CommonMark\HtmlElement;
abstract class CodeRenderer implements BlockRendererInterface abstract class CodeRenderer implements BlockRendererInterface
{ {
public function escapeCDATA($content) public function escapeCDATA($content)
{ {
return str_replace(']]>', ']]]]><![CDATA[>', $content); return str_replace("]]>", "]]]]><![CDATA[>", $content);
} }
public function getHTMLElement($body, $language) public function getHTMLElement($body, $language)

View File

@ -1,10 +1,7 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown; <?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use League\CommonMark\Block\Element as BlockElement;
use League\CommonMark\Environment; use League\CommonMark\Environment;
use League\CommonMark\Inline\Element as InlineElement;
use Todaymade\Daux\Config; use Todaymade\Daux\Config;
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter
{ {
@ -17,12 +14,12 @@ class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMa
{ {
parent::extendEnvironment($environment, $config); parent::extendEnvironment($environment, $config);
$environment->addBlockRenderer(TableOfContents::class, new TOCRenderer()); $environment->addBlockRenderer('Todaymade\Daux\ContentTypes\Markdown\TableOfContents', new TOCRenderer());
//Add code renderer //Add code renderer
$environment->addBlockRenderer(BlockElement\FencedCode::class, new FencedCodeRenderer()); $environment->addBlockRenderer('FencedCode', new FencedCodeRenderer());
$environment->addBlockRenderer(BlockElement\IndentedCode::class, new IndentedCodeRenderer()); $environment->addBlockRenderer('IndentedCode', new IndentedCodeRenderer());
$environment->addInlineRenderer(InlineElement\Image::class, new ImageRenderer()); $environment->addInlineRenderer('Image', new ImageRenderer());
} }
} }

View File

@ -1,9 +1,12 @@
<?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown; <?php namespace Todaymade\Daux\Format\Confluence\ContentTypes\Markdown;
use Todaymade\Daux\Config;
class ContentType extends \Todaymade\Daux\ContentTypes\Markdown\ContentType 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]);
} }
} }

View File

@ -4,7 +4,6 @@ use League\CommonMark\Block\Element\AbstractBlock;
use League\CommonMark\Block\Element\FencedCode; use League\CommonMark\Block\Element\FencedCode;
use League\CommonMark\ElementRendererInterface; use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement; use League\CommonMark\HtmlElement;
use League\CommonMark\Util\Xml;
class FencedCodeRenderer extends CodeRenderer class FencedCodeRenderer extends CodeRenderer
{ {
@ -36,6 +35,8 @@ class FencedCodeRenderer extends CodeRenderer
protected $known_conversions = ['html' => 'html/xml', 'xml' => 'html/xml', 'js' => 'javascript']; protected $known_conversions = ['html' => 'html/xml', 'xml' => 'html/xml', 'js' => 'javascript'];
/** /**
* @param AbstractBlock $block
* @param HtmlRendererInterface $htmlRenderer
* @param bool $inTightList * @param bool $inTightList
* *
* @return HtmlElement|string * @return HtmlElement|string
@ -46,18 +47,18 @@ class FencedCodeRenderer extends CodeRenderer
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block)); throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
} }
$language = $this->getLanguage($block->getInfoWords()); $language = $this->getLanguage($block->getInfoWords(), $htmlRenderer);
return $this->getHTMLElement($block->getStringContent(), $language); return $this->getHTMLElement($block->getStringContent(), $language);
} }
public function getLanguage($infoWords) public function getLanguage($infoWords, ElementRendererInterface $htmlRenderer)
{ {
if (count($infoWords) === 0 || strlen($infoWords[0]) === 0) { if (count($infoWords) === 0 || strlen($infoWords[0]) === 0) {
return false; return false;
} }
$language = Xml::escape($infoWords[0]); $language = $htmlRenderer->escape($infoWords[0], true);
if (array_key_exists($language, $this->known_conversions)) { if (array_key_exists($language, $this->known_conversions)) {
$language = $this->known_conversions[$language]; $language = $this->known_conversions[$language];

View File

@ -4,29 +4,12 @@ use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement; use League\CommonMark\HtmlElement;
use League\CommonMark\Inline\Element\AbstractInline; use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Image; 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 Image $inline
* @param ElementRendererInterface $htmlRenderer
* *
* @return HtmlElement * @return HtmlElement
*/ */
@ -45,11 +28,6 @@ class ImageRenderer implements InlineRendererInterface, ConfigurationAwareInterf
); );
} }
return $this->parent->render($inline, $htmlRenderer); return parent::render($inline, $htmlRenderer);
}
public function setConfiguration(ConfigurationInterface $configuration)
{
$this->parent->setConfiguration($configuration);
} }
} }

View File

@ -8,6 +8,8 @@ use League\CommonMark\HtmlElement;
class IndentedCodeRenderer extends CodeRenderer class IndentedCodeRenderer extends CodeRenderer
{ {
/** /**
* @param AbstractBlock $block
* @param HtmlRendererInterface $htmlRenderer
* @param bool $inTightList * @param bool $inTightList
* *
* @return HtmlElement * @return HtmlElement
@ -18,6 +20,6 @@ class IndentedCodeRenderer extends CodeRenderer
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block)); throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
} }
return $this->getHTMLElement($block->getStringContent(), ''); return $this->getHTMLElement($block->getStringContent(), "");
} }
} }

View File

@ -4,19 +4,26 @@ use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement; use League\CommonMark\HtmlElement;
use League\CommonMark\Inline\Element\AbstractInline; use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Link; use League\CommonMark\Inline\Element\Link;
use Todaymade\Daux\DauxHelper;
class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
{ {
/** /**
* @param AbstractInline|Link $inline * @param AbstractInline|Link $inline
* @param ElementRendererInterface $htmlRenderer
* *
* @return HtmlElement * @return HtmlElement
*/ */
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer) public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
{ {
if (!($inline instanceof Link)) { // This can't be in the method type as
throw new \InvalidArgumentException('Incompatible inline type: ' . \get_class($inline)); // 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 // Default handling
@ -26,29 +33,19 @@ class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
// empty urls, anchors and absolute urls // empty urls, anchors and absolute urls
// should not go through the url resolver // should not go through the url resolver
if (!DauxHelper::isValidUrl($url) || DauxHelper::isExternalUrl($url)) { if (!$this->isValidUrl($url) || $this->isExternalUrl($url)) {
return $element; 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 //Internal links
$file = DauxHelper::resolveInternalFile($this->daux, $url); $file = $this->resolveInternalFile($url);
$link_props = []; $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()), 'ri:content-title' => trim(trim($this->daux['confluence']['prefix']) . ' ' . $file->getTitle()),
'ri:space-key' => $this->daux['confluence']['space_id'], '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()); $children = $htmlRenderer->renderInlines($inline->children());
if (strpos($children, '<') !== false) { if (strpos($children, '<') !== false) {
$children = '<ac:link-body>' . $children . '</ac:link-body>'; $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>'; $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);
} }
} }

View File

@ -3,16 +3,11 @@
use League\CommonMark\Block\Element\AbstractBlock; use League\CommonMark\Block\Element\AbstractBlock;
use League\CommonMark\Block\Renderer\BlockRendererInterface; use League\CommonMark\Block\Renderer\BlockRendererInterface;
use League\CommonMark\ElementRendererInterface; use League\CommonMark\ElementRendererInterface;
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
class TOCRenderer implements BlockRendererInterface class TOCRenderer implements BlockRendererInterface
{ {
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false) 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>'; return '<ac:structured-macro ac:name="toc"></ac:structured-macro>';
} }
} }

View File

@ -2,7 +2,7 @@
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Config as GlobalConfig; use Todaymade\Daux\Config;
use Todaymade\Daux\Console\RunAction; use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
use Todaymade\Daux\Tree\Content; use Todaymade\Daux\Tree\Content;
@ -18,6 +18,9 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
/** @var Daux */ /** @var Daux */
protected $daux; protected $daux;
/**
* @param Daux $daux
*/
public function __construct(Daux $daux) public function __construct(Daux $daux)
{ {
$this->daux = $daux; $this->daux = $daux;
@ -27,7 +30,7 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
public function checkConfiguration() public function checkConfiguration()
{ {
$config = $this->daux->getConfig(); $config = $this->daux->getParams();
$confluence = $config->getConfluenceConfiguration(); $confluence = $config->getConfluenceConfiguration();
if ($confluence == null) { if ($confluence == null) {
@ -37,7 +40,7 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
$mandatory = ['space_id', 'base_url', 'user', 'pass', 'prefix']; $mandatory = ['space_id', 'base_url', 'user', 'pass', 'prefix'];
$errors = []; $errors = [];
foreach ($mandatory as $key) { foreach ($mandatory as $key) {
if (!$confluence->hasValue($key)) { if (!array_key_exists($key, $confluence)) {
$errors[] = $key; $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) . "'"); 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."); 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() public function getContentTypes()
{ {
return [ return [
new ContentTypes\Markdown\ContentType($this->daux->getConfig()), new ContentTypes\Markdown\ContentType($this->daux->getParams()),
]; ];
} }
@ -66,10 +69,10 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
*/ */
public function generateAll(InputInterface $input, OutputInterface $output, $width) public function generateAll(InputInterface $input, OutputInterface $output, $width)
{ {
$config = $this->daux->getConfig(); $params = $this->daux->getParams();
$confluence = $config->getConfluenceConfiguration(); $confluence = $params['confluence'];
$this->prefix = trim($confluence->getPrefix()) . ' '; $this->prefix = trim($confluence['prefix']) . ' ';
if ($this->prefix == ' ') { if ($this->prefix == ' ') {
$this->prefix = ''; $this->prefix = '';
} }
@ -77,9 +80,9 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
$tree = $this->runAction( $tree = $this->runAction(
'Generating Tree ...', 'Generating Tree ...',
$width, $width,
function () use ($config) { function() use ($params) {
$tree = $this->generateRecursive($this->daux->tree, $config); $tree = $this->generateRecursive($this->daux->tree, $params);
$tree['title'] = $this->prefix . $config->getTitle(); $tree['title'] = $this->prefix . $params['title'];
return $tree; return $tree;
} }
@ -93,31 +96,31 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
$publisher->publish($tree); $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()]; $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 !== '') { if ($base_url !== '') {
$config->setEntryPage($tree->getFirstPage()); $params['entry_page'] = $tree->getFirstPage();
} }
foreach ($tree->getEntries() as $key => $node) { foreach ($tree->getEntries() as $key => $node) {
if ($node instanceof Directory) { if ($node instanceof Directory) {
$final['children'][$this->prefix . $node->getTitle()] = $this->generateRecursive( $final['children'][$this->prefix . $node->getTitle()] = $this->generateRecursive(
$node, $node,
$config, $params,
'../' . $base_url '../' . $base_url
); );
} elseif ($node instanceof Content) { } elseif ($node instanceof Content) {
$config->setRequest($node->getUrl()); $params['request'] = $node->getUrl();
$contentType = $this->daux->getContentTypeHandler()->getType($node); $contentType = $this->daux->getContentTypeHandler()->getType($node);
$data = [ $data = [
'title' => $this->prefix . $node->getTitle(), 'title' => $this->prefix . $node->getTitle(),
'file' => $node, 'file' => $node,
'page' => ContentPage::fromFile($node, $config, $contentType), 'page' => ContentPage::fromFile($node, $params, $contentType),
]; ];
if ($key == 'index.html') { if ($key == 'index.html') {

View File

@ -7,6 +7,16 @@ class Publisher
{ {
use RunAction; use RunAction;
/**
* @var Api
*/
protected $client;
/**
* @var array
*/
protected $confluence;
/** /**
* @var int terminal width * @var int terminal width
*/ */
@ -17,25 +27,15 @@ class Publisher
*/ */
public $output; public $output;
/**
* @var Api
*/
protected $client;
/**
* @var Config
*/
protected $confluence;
/** /**
* @param $confluence * @param $confluence
*/ */
public function __construct(Config $confluence) public function __construct($confluence)
{ {
$this->confluence = $confluence; $this->confluence = $confluence;
$this->client = new Api($confluence->getBaseUrl(), $confluence->getUser(), $confluence->getPassword()); $this->client = new Api($confluence['base_url'], $confluence['user'], $confluence['pass']);
$this->client->setSpace($confluence->getSpaceId()); $this->client->setSpace($confluence['space_id']);
} }
public function run($title, $closure) public function run($title, $closure)
@ -62,23 +62,24 @@ class Publisher
); );
$published = $this->run( $published = $this->run(
'Create placeholder pages...', "Create placeholder pages...",
function () use ($tree, $published) { 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...'); $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); $shouldDelete = array_key_exists('delete', $this->confluence) && $this->confluence['delete'];
$delete = new PublisherDelete($this->output, $shouldDelete, $this->client);
$delete->handle($published); $delete->handle($published);
} }
protected function getRootPage($tree) protected function getRootPage($tree)
{ {
if ($this->confluence->hasAncestorId()) { if (array_key_exists('ancestor_id', $this->confluence)) {
$pages = $this->client->getList($this->confluence->getAncestorId()); $pages = $this->client->getList($this->confluence['ancestor_id']);
foreach ($pages as $page) { foreach ($pages as $page) {
if ($page['title'] == $tree['title']) { if ($page['title'] == $tree['title']) {
return $page; return $page;
@ -86,10 +87,9 @@ class Publisher
} }
} }
if ($this->confluence->hasRootId()) { if (array_key_exists('root_id', $this->confluence)) {
$published = $this->client->getPage($this->confluence->getRootId()); $published = $this->client->getPage($this->confluence['root_id']);
$this->confluence->setAncestorId($published['ancestor_id']); $this->confluence['ancestor_id'] = $published['ancestor_id'];
return $published; return $published;
} }
@ -178,7 +178,7 @@ class Publisher
protected function updatePage($parent_id, $entry, $published) protected function updatePage($parent_id, $entry, $published)
{ {
$updateThreshold = $this->confluence->getUpdateThreshold(); $updateThreshold = array_key_exists('update_threshold', $this->confluence) ? $this->confluence['update_threshold'] : 2;
$this->run( $this->run(
'- ' . PublisherUtilities::niceTitle($entry['file']->getUrl()), '- ' . PublisherUtilities::niceTitle($entry['file']->getUrl()),

View File

@ -13,7 +13,7 @@ class PublisherDelete
protected $deletable; protected $deletable;
/** /**
* @var bool should delete ? * @var boolean should delete ?
*/ */
protected $delete; protected $delete;
@ -22,12 +22,13 @@ class PublisherDelete
*/ */
protected $client; protected $client;
public function __construct($output, bool $delete, $client) public function __construct($output, $delete, $client)
{ {
$this->output = $output; $this->output = $output;
$this->delete = $delete; $this->delete = $delete;
$this->client = $client; $this->client = $client;
$this->deletable = []; $this->deletable = [];
} }
@ -54,13 +55,13 @@ class PublisherDelete
if ($this->delete) { if ($this->delete) {
$this->doDelete(); $this->doDelete();
} else { } else {
$this->displayDeletable(); $this->displayDeletable();
} }
} }
protected function doDelete() protected function doDelete() {
{
$this->output->writeLn('Deleting obsolete pages...'); $this->output->writeLn('Deleting obsolete pages...');
foreach ($this->deletable as $id => $title) { foreach ($this->deletable as $id => $title) {
$this->output->writeLn("- $title"); $this->output->writeLn("- $title");
@ -68,11 +69,10 @@ class PublisherDelete
} }
} }
protected function displayDeletable() protected function displayDeletable() {
{
$this->output->writeLn('Listing obsolete pages...'); $this->output->writeLn('Listing obsolete pages...');
$this->output->writeLn('> The following pages will not be deleted, but just listed for information.'); $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.'); $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) { foreach ($this->deletable as $id => $title) {
$this->output->writeLn("- $title"); $this->output->writeLn("- $title");
} }

View File

@ -10,182 +10,27 @@ class Config extends BaseConfig
return [ return [
'name' => 'GitHub', '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() public function getEditOn()
{ {
if ($this->hasValue('edit_on')) { if (array_key_exists('edit_on', $this)) {
$edit_on = $this->getValue('edit_on'); if (is_string($this['edit_on'])) {
if (is_string($edit_on)) { return $this->prepareGithubUrl($this['edit_on']);
return $this->prepareGithubUrl($edit_on); } else {
}
$edit_on['basepath'] = rtrim($edit_on['basepath'], '/');
return $edit_on; $this['edit_on']['basepath'] = rtrim($this['edit_on']['basepath'], '/');
return $this['edit_on'];
}
} }
if ($this->hasValue('edit_on_github')) { if (array_key_exists('edit_on_github', $this)) {
return $this->prepareGithubUrl($this->getValue('edit_on_github')); return $this->prepareGithubUrl($this['edit_on_github']);
} }
return null; 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');
}
} }

View File

@ -4,34 +4,27 @@ use Todaymade\Daux\Tree\Root;
class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
{ {
/**
* @var Template
*/
public $templateRenderer;
/**
* @var string
*/
private $language; private $language;
/**
* @var bool
*/
private $homepage; 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 the current page isn't the index, no chance it is the landing page
if ($this->file->getParent()->getIndexPage() != $this->file) { if ($this->file->getParent()->getIndexPage() != $this->file) {
return false; 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; return $this->file->getParent() instanceof Root;
} }
private function isLanding(): bool private function isLanding() {
{ // If we don't have the auto_landing parameter, we don't want any homepage
return $this->config->getHTML()->hasLandingPage() && $this->homepage; if (array_key_exists('auto_landing', $this->params['html']) && !$this->params['html']['auto_landing']) {
return false;
}
return $this->homepage;
} }
private function initialize() private function initialize()
@ -39,7 +32,7 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
$this->homepage = $this->isHomepage(); $this->homepage = $this->isHomepage();
$this->language = ''; $this->language = '';
if ($this->config->isMultilanguage() && count($this->file->getParents())) { if ($this->params->isMultilanguage() && count($this->file->getParents())) {
$language_dir = $this->file->getParents()[0]; $language_dir = $this->file->getParents()[0];
$this->language = $language_dir->getName(); $this->language = $language_dir->getName();
} }
@ -48,7 +41,6 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
/** /**
* @param \Todaymade\Daux\Tree\Directory[] $parents * @param \Todaymade\Daux\Tree\Directory[] $parents
* @param bool $multilanguage * @param bool $multilanguage
*
* @return array * @return array
*/ */
private function getBreadcrumbTrail($parents, $multilanguage) private function getBreadcrumbTrail($parents, $multilanguage)
@ -70,16 +62,16 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
protected function generatePage() protected function generatePage()
{ {
$this->initialize(); $this->initialize();
$config = $this->config; $params = $this->params;
$entry_page = []; $entry_page = [];
if ($this->homepage) { if ($this->homepage) {
if ($config->isMultilanguage()) { if ($params->isMultilanguage()) {
foreach ($config->getLanguages() as $key => $name) { foreach ($params['languages'] as $key => $name) {
$entry_page[$name] = $config->getBasePage() . $config->getEntryPage()[$key]->getUrl(); $entry_page[$name] = $params['base_page'] . $params['entry_page'][$key]->getUrl();
} }
} else { } else {
$entry_page['__VIEW_DOCUMENTATION__'] = $config->getBasePage() . $config->getEntryPage()->getUrl(); $entry_page['__VIEW_DOCUMENTATION__'] = $params['base_page'] . $params['entry_page']->getUrl();
} }
} }
@ -93,34 +85,25 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
'relative_path' => $this->file->getRelativePath(), 'relative_path' => $this->file->getRelativePath(),
'modified_time' => filemtime($this->file->getPath()), 'modified_time' => filemtime($this->file->getPath()),
'markdown' => $this->content, 'markdown' => $this->content,
'request' => $config->getRequest(), 'request' => $params['request'],
'content' => $this->getPureContent(), 'content' => $this->getPureContent(),
'breadcrumbs' => $config->getHTML()->hasBreadcrumbs(), 'breadcrumbs' => $params['html']['breadcrumbs'],
'prev' => $this->file->getPrevious(), 'prev' => $this->file->getPrevious(),
'next' => $this->file->getNext(), 'next' => $this->file->getNext(),
'attributes' => $this->file->getAttribute(), 'attributes' => $this->file->getAttribute()
]; ];
if ($page['breadcrumbs']) { if ($page['breadcrumbs']) {
$page['breadcrumb_trail'] = $this->getBreadcrumbTrail($this->file->getParents(), $config->isMultilanguage()); $page['breadcrumb_trail'] = $this->getBreadcrumbTrail($this->file->getParents(), $params->isMultilanguage());
$page['breadcrumb_separator'] = $this->config->getHTML()->getBreadcrumbsSeparator(); $page['breadcrumb_separator'] = $params['html']['breadcrumb_separator'];
if ($this->homepage) { if ($this->homepage) {
$page['breadcrumb_trail'] = [['title' => $this->file->getTitle(), 'url' => '']]; $page['breadcrumb_trail'] = [['title' => $this->file->getTitle(), 'url' => '']];
} }
} }
$context = ['page' => $page, 'config' => $config]; $context = ['page' => $page, 'params' => $params];
$template = 'theme::content'; return $this->templateRenderer->render($this->isLanding() ? 'theme::home' : 'theme::content', $context);
if ($this->isLanding()) {
$template = 'theme::home';
}
if (array_key_exists('template', $page['attributes'])) {
$template = 'theme::' . $page['attributes']['template'];
}
return $this->templateRenderer->render($template, $context);
} }
} }

View File

@ -1,11 +1,7 @@
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown; <?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
use League\CommonMark\Block\Element as BlockElement;
use League\CommonMark\Environment; use League\CommonMark\Environment;
use League\CommonMark\Event\DocumentParsedEvent;
use League\CommonMark\Inline\Element as InlineElement;
use Todaymade\Daux\Config; use Todaymade\Daux\Config;
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMarkConverter
{ {
@ -13,12 +9,7 @@ class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMa
{ {
parent::extendEnvironment($environment, $config); parent::extendEnvironment($environment, $config);
$environment->addBlockRenderer(BlockElement\FencedCode::class, new FencedCodeRenderer()); $environment->addDocumentProcessor(new TOC\Processor($config));
$environment->addBlockRenderer('Todaymade\Daux\ContentTypes\Markdown\TableOfContents', new TOC\Renderer($config));
$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));
} }
} }

View File

@ -1,9 +1,12 @@
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown; <?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
use Todaymade\Daux\Config;
class ContentType extends \Todaymade\Daux\ContentTypes\Markdown\ContentType 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]);
} }
} }

View File

@ -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]);
}
}

View File

@ -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);
}
}

View File

@ -6,7 +6,7 @@ class Entry
{ {
protected $content; protected $content;
protected $level; protected $level;
protected $parent; protected $parent = null;
protected $children = []; protected $children = [];
public function __construct(Heading $content) public function __construct(Heading $content)
@ -56,6 +56,7 @@ class Entry
} }
/** /**
* @param Entry $parent
* @param bool $addChild * @param bool $addChild
*/ */
public function setParent(Entry $parent, $addChild = true) public function setParent(Entry $parent, $addChild = true)
@ -66,6 +67,9 @@ class Entry
} }
} }
/**
* @param Entry $child
*/
public function addChild(Entry $child) public function addChild(Entry $child)
{ {
$child->setParent($this, false); $child->setParent($this, false);

View File

@ -7,20 +7,16 @@ use League\CommonMark\Block\Element\ListBlock;
use League\CommonMark\Block\Element\ListData; use League\CommonMark\Block\Element\ListData;
use League\CommonMark\Block\Element\ListItem; use League\CommonMark\Block\Element\ListItem;
use League\CommonMark\Block\Element\Paragraph; 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\Link;
use League\CommonMark\Inline\Element\Text; use League\CommonMark\Inline\Element\Text;
use League\CommonMark\Node\Node; use League\CommonMark\Node\Node;
use ReflectionMethod; use ReflectionMethod;
use Todaymade\Daux\Config; use Todaymade\Daux\Config;
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents; use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
use Todaymade\Daux\DauxHelper;
class Processor class Processor implements DocumentProcessorInterface
{ {
/**
* @var Config
*/
protected $config; protected $config;
public function __construct(Config $config) public function __construct(Config $config)
@ -28,9 +24,18 @@ class Processor
$this->config = $config; $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 */ /** @var TableOfContents[] $tocs */
$tocs = []; $tocs = [];
@ -43,7 +48,6 @@ class Processor
if ($node instanceof TableOfContents && !$event->isEntering()) { if ($node instanceof TableOfContents && !$event->isEntering()) {
$tocs[] = $node; $tocs[] = $node;
continue; continue;
} }
@ -55,7 +59,7 @@ class Processor
$headings[] = new Entry($node); $headings[] = new Entry($node);
} }
if (count($headings) && (count($tocs) || $this->config->getHTML()->hasAutomaticTableOfContents())) { if (count($headings) && (count($tocs) || $this->hasAutoTOC())) {
$generated = $this->generate($headings); $generated = $this->generate($headings);
if (count($tocs)) { if (count($tocs)) {
@ -68,27 +72,43 @@ class Processor
} }
} }
protected function getUniqueId(Document $document, $proposed) /**
{ * Get an escaped version of the link
if ($proposed == 'page_') { * @param string $url
$proposed = 'page_section_' . (count($document->heading_ids) + 1); * @return string
*/
protected function escaped($url) {
$url = trim($url);
$url = preg_replace('~[^\\pL0-9_]+~u', '-', $url);
$url = trim($url, "-");
$url = iconv("utf-8", "ASCII//TRANSLIT//IGNORE", $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 // Quick path, it's a unique ID
if (!in_array($proposed, $document->heading_ids)) { if (!in_array($proposed, $document->heading_ids)) {
$document->heading_ids[] = $proposed; $document->heading_ids[] = $proposed;
return $proposed; return $proposed;
} }
$extension = 1; // Initialize the variable at one, so on the first iteration we have 2 $extension = 1; // Initialize the variable at one, so on the first iteration we have 2
do { do {
++$extension; $extension++;
} while (in_array("$proposed-$extension", $document->heading_ids)); } while (in_array("$proposed-$extension", $document->heading_ids));
return "$proposed-$extension"; return "$proposed-$extension";
} }
/**
* @param Heading $node
*/
protected function ensureHeadingHasId(Document $document, 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 // 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 * @param Entry[] $headings
*
* @return RootEntry * @return RootEntry
*/ */
public function generate($headings) public function generate($headings)
@ -142,21 +161,19 @@ class Processor
$parent->addChild($heading); $parent->addChild($heading);
$previous = $heading; $previous = $heading;
continue; continue;
} }
if ($heading->getLevel() > $previous->getLevel()) { if ($heading->getLevel() > $previous->getLevel()) {
$previous->addChild($heading); $previous->addChild($heading);
$previous = $heading; $previous = $heading;
continue; continue;
} }
//if ($heading->getLevel() == $previous->getLevel()) { //if ($heading->getLevel() == $previous->getLevel()) {
$previous->getParent()->addChild($heading); $previous->getParent()->addChild($heading);
$previous = $heading; $previous = $heading;
continue; continue;
//} //}
} }
@ -166,7 +183,6 @@ class Processor
/** /**
* @param Entry[] $entries * @param Entry[] $entries
*
* @return ListBlock * @return ListBlock
*/ */
protected function render(array $entries) protected function render(array $entries)
@ -218,6 +234,7 @@ class Processor
} }
/** /**
* @param Heading $node
* @return Node[] * @return Node[]
*/ */
protected function cloneChildren(Heading $node) protected function cloneChildren(Heading $node)
@ -237,6 +254,8 @@ class Processor
$method->invoke($subnode, $firstClone); $method->invoke($subnode, $firstClone);
} }
return (new DeepCopy())->copy($firstClone)->children(); $deepCopy = new DeepCopy();
return $deepCopy->copy($firstClone)->children();
} }
} }

View File

@ -4,15 +4,9 @@ use League\CommonMark\Block\Element\AbstractBlock;
use League\CommonMark\Block\Renderer\BlockRendererInterface; use League\CommonMark\Block\Renderer\BlockRendererInterface;
use League\CommonMark\ElementRendererInterface; use League\CommonMark\ElementRendererInterface;
use Todaymade\Daux\Config; use Todaymade\Daux\Config;
use Todaymade\Daux\ContentTypes\Markdown\TableOfContents;
class Renderer implements BlockRendererInterface class Renderer implements BlockRendererInterface
{ {
/**
* @var Config
*/
private $config;
public function __construct(Config $config) public function __construct(Config $config)
{ {
$this->config = $config; $this->config = $config;
@ -20,14 +14,9 @@ class Renderer implements BlockRendererInterface
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false) 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()); $content = $htmlRenderer->renderBlocks($block->children());
return $this->config->templateRenderer return $this->config->templateRenderer
->getEngine($this->config) ->getEngine($this->config)
->render('theme::partials/table_of_contents', ['content' => $content]); ->render('partials/table_of_contents', ['content' => $content]);
} }
} }

View File

@ -2,7 +2,7 @@
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Config as GlobalConfig; use Todaymade\Daux\Config;
use Todaymade\Daux\Console\RunAction; use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
use Todaymade\Daux\DauxHelper; use Todaymade\Daux\DauxHelper;
@ -17,23 +17,22 @@ use Todaymade\Daux\Tree\Raw;
class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
{ {
use RunAction; use RunAction;
use HTMLUtils;
/** @var Daux */ /** @var Daux */
protected $daux; protected $daux;
/** @var Template */
protected $templateRenderer;
protected $indexed_pages = []; protected $indexed_pages = [];
/**
* @param Daux $daux
*/
public function __construct(Daux $daux) public function __construct(Daux $daux)
{ {
$config = $daux->getConfig(); $params = $daux->getParams();
$this->daux = $daux; $this->daux = $daux;
$this->templateRenderer = new Template($config); $this->templateRenderer = new Template($params);
$config->templateRenderer = $this->templateRenderer; $params->templateRenderer = $this->templateRenderer;
} }
/** /**
@ -42,42 +41,69 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
public function getContentTypes() public function getContentTypes()
{ {
return [ 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) public function generateAll(InputInterface $input, OutputInterface $output, $width)
{ {
$destination = $input->getOption('destination'); $destination = $input->getOption('destination');
$config = $this->daux->getConfig(); $params = $this->daux->getParams();
if (is_null($destination)) { if (is_null($destination)) {
$destination = $config->getLocalBase() . DIRECTORY_SEPARATOR . 'static'; $destination = $this->daux->local_base . DIRECTORY_SEPARATOR . 'static';
} }
$this->runAction( $this->runAction(
'Copying Static assets ...', 'Copying Static assets ...',
$width, $width,
function () use ($destination, $config) { function() use ($destination, $params) {
$this->ensureEmptyDestination($destination); $this->ensureEmptyDestination($destination);
$this->copyThemes($destination, $config->getThemesPath()); $this->copyThemes($destination, $params->getThemesPath());
} }
); );
$output->writeLn('Generating ...'); $output->writeLn('Generating ...');
$this->generateRecursive($this->daux->tree, $destination, $config, $output, $width, $config->getHTML()->hasSearch()); if (!array_key_exists('search', $params['html']) || !$params['html']['search']) {
$params['html']['search'] = $input->getOption('search');
}
GeneratorHelper::copyRecursive( $this->generateRecursive($this->daux->tree, $destination, $params, $output, $width, $params['html']['search']);
$config->getLocalBase() . DIRECTORY_SEPARATOR . 'daux_libraries' . DIRECTORY_SEPARATOR,
$destination . DIRECTORY_SEPARATOR . 'daux_libraries'
);
if ($config->getHTML()->hasSearch()) { if ($params['html']['search']) {
GeneratorHelper::copyRecursive(
$this->daux->local_base . DIRECTORY_SEPARATOR . 'tipuesearch' . DIRECTORY_SEPARATOR,
$destination . DIRECTORY_SEPARATOR . 'tipuesearch'
);
file_put_contents( file_put_contents(
$destination . DIRECTORY_SEPARATOR . 'daux_search_index.js', $destination . DIRECTORY_SEPARATOR . 'tipuesearch' . DIRECTORY_SEPARATOR . 'tipuesearch_content.json',
'load_search_index(' . json_encode(['pages' => $this->indexed_pages]) . ');' json_encode(['pages' => $this->indexed_pages])
); );
if (json_last_error()) { if (json_last_error()) {
@ -91,10 +117,9 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
* script code, and embedded objects. Add line breaks around * script code, and embedded objects. Add line breaks around
* block-level tags to prevent word joining after tag removal. * block-level tags to prevent word joining after tag removal.
* Also collapse whitespace to single space and trim result. * 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 * @param string $text
*
* @return string * @return string
*/ */
private function sanitize($text) private function sanitize($text)
@ -132,41 +157,44 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
// Sometimes strings are detected as invalid UTF-8 and json_encode can't treat them // Sometimes strings are detected as invalid UTF-8 and json_encode can't treat them
// iconv can fix those strings // iconv can fix those strings
return iconv('UTF-8', 'UTF-8//IGNORE', $text); $text = iconv('UTF-8', 'UTF-8//IGNORE', $text);
return $text;
} }
/** /**
* Recursively generate the documentation. * Recursively generate the documentation
* *
* @param Directory $tree
* @param string $output_dir * @param string $output_dir
* @param \Todaymade\Daux\Config $params
* @param OutputInterface $output * @param OutputInterface $output
* @param int $width * @param int $width
* @param bool $index_pages * @param bool $index_pages
* @param string $base_url * @param string $base_url
*
* @throws \Exception * @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()) { if ($base_url !== '' && empty($params['entry_page'])) {
$config->setEntryPage($tree->getFirstPage()); $params['entry_page'] = $tree->getFirstPage();
} }
foreach ($tree->getEntries() as $key => $node) { foreach ($tree->getEntries() as $key => $node) {
if ($node instanceof Directory) { if ($node instanceof Directory) {
$new_output_dir = $output_dir . DIRECTORY_SEPARATOR . $key; $new_output_dir = $output_dir . DIRECTORY_SEPARATOR . $key;
mkdir($new_output_dir); 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 // Rebase configuration again as $params is a shared object
DauxHelper::rebaseConfiguration($config, $base_url); DauxHelper::rebaseConfiguration($params, $base_url);
} else { } else {
$this->runAction( $this->runAction(
'- ' . $node->getUrl(), '- ' . $node->getUrl(),
$width, $width,
function () use ($node, $output_dir, $key, $config, $index_pages) { function() use ($node, $output_dir, $key, $params, $index_pages) {
if ($node instanceof Raw) { if ($node instanceof Raw) {
copy($node->getPath(), $output_dir . DIRECTORY_SEPARATOR . $key); copy($node->getPath(), $output_dir . DIRECTORY_SEPARATOR . $key);
@ -175,13 +203,13 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
$this->daux->tree->setActiveNode($node); $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()); file_put_contents($output_dir . DIRECTORY_SEPARATOR . $key, $generated->getContent());
if ($index_pages) { if ($index_pages) {
$this->indexed_pages[] = [ $this->indexed_pages[] = [
'title' => $node->getTitle(), 'title' => $node->getTitle(),
'text' => $this->sanitize($generated->getPureContent()), 'text' => $this->sanitize($generated->getPureContent()),
'tags' => '', 'tags' => '',
'url' => $node->getUrl(), 'url' => $node->getUrl(),
]; ];
} }
@ -192,9 +220,11 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
} }
/** /**
* @param Entry $node
* @param Config $params
* @return \Todaymade\Daux\Format\Base\Page * @return \Todaymade\Daux\Format\Base\Page
*/ */
public function generateOne(Entry $node, GlobalConfig $config) public function generateOne(Entry $node, Config $params)
{ {
if ($node instanceof Raw) { if ($node instanceof Raw) {
return new RawPage($node->getPath()); return new RawPage($node->getPath());
@ -204,9 +234,9 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
return new ComputedRawPage($node); return new ComputedRawPage($node);
} }
$config->setRequest($node->getUrl()); $params['request'] = $node->getUrl();
$contentPage = ContentPage::fromFile($node, $config, $this->daux->getContentTypeHandler()->getType($node)); $contentPage = ContentPage::fromFile($node, $params, $this->daux->getContentTypeHandler()->getType($node));
$contentPage->templateRenderer = $this->templateRenderer; $contentPage->templateRenderer = $this->templateRenderer;
return $contentPage; return $contentPage;

View File

@ -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'
);
}
}

View File

@ -1,10 +1,8 @@
<?php <?php namespace Todaymade\Daux\Format\HTML;
namespace Todaymade\Daux\Format\HTML;
use League\Plates\Engine; use League\Plates\Engine;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Config as GlobalConfig; use Todaymade\Daux\Config;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
use Todaymade\Daux\Tree\Content; use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory; use Todaymade\Daux\Tree\Directory;
@ -13,25 +11,25 @@ class Template
{ {
protected $engine; protected $engine;
protected $config; protected $params;
/** /**
* @param string $base * @param string $base
* @param string $theme * @param string $theme
*/ */
public function __construct(GlobalConfig $config) public function __construct(Config $params)
{ {
$this->config = $config; $this->params = $params;
} }
public function getEngine(GlobalConfig $config) public function getEngine(Config $params)
{ {
if ($this->engine) { if ($this->engine) {
return $this->engine; return $this->engine;
} }
$base = $config->getTemplates(); $base = $params['templates'];
$theme = $config->getTheme()->getTemplates(); $theme = $params['theme']['templates'];
// Use internal templates if no templates // Use internal templates if no templates
// dir exists in the working directory // dir exists in the working directory
@ -55,20 +53,19 @@ class Template
/** /**
* @param string $name * @param string $name
* * @param array $data
* @return string * @return string
*/ */
public function render($name, array $data = []) public function render($name, array $data = [])
{ {
$engine = $this->getEngine($data['config']); $engine = $this->getEngine($data['params']);
$engine->addData([ $engine->addData([
'base_url' => $data['config']->getBaseUrl(), 'base_url' => $data['params']['base_url'],
'base_page' => $data['config']->getBasePage(), 'base_page' => $data['params']['base_page'],
'page' => $data['page'], 'page' => $data['page'],
'params' => $data['config'], // legacy name for config 'params' => $data['params'],
'config' => $data['config'], 'tree' => $data['params']['tree'],
'tree' => $data['config']['tree'],
]); ]);
Daux::writeln("Rendering template '$name'", OutputInterface::VERBOSITY_VERBOSE); Daux::writeln("Rendering template '$name'", OutputInterface::VERBOSITY_VERBOSE);
@ -85,21 +82,14 @@ class Template
}); });
$engine->registerFunction('translate', function ($key) { $engine->registerFunction('translate', function ($key) {
$language = $this->config->getLanguage(); $language = $this->params['language'];
if (isset($this->engine->getData('page')['page'])) { if (array_key_exists($key, $this->params['strings'][$language])) {
$page = $this->engine->getData('page'); return $this->params['strings'][$language][$key];
if (!empty($page['page']['language'])) {
$language = $page['page']['language'];
}
} }
if ($this->config->hasTranslationKey($language, $key)) { if (array_key_exists($key, $this->params['strings']['en'])) {
return $this->config->getTranslationKey($language, $key); return $this->params['strings']['en'][$key];
}
if ($this->config->hasTranslationKey('en', $key)) {
return $this->config->getTranslationKey('en', $key);
} }
return "Unknown key $key"; return "Unknown key $key";
@ -132,9 +122,9 @@ class Template
$icon = '<i class="Nav__arrow">&nbsp;</i>'; $icon = '<i class="Nav__arrow">&nbsp;</i>';
if (array_key_exists('href', $entry)) { 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 { } 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']); $link .= $this->renderNavigation($entry['children']);
@ -196,7 +186,6 @@ class Template
/** /**
* @param string $separator * @param string $separator
*
* @return string * @return string
*/ */
private function getSeparator($separator) private function getSeparator($separator)

View File

@ -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');
}
}

View File

@ -1,5 +1,6 @@
<?php namespace Todaymade\Daux\Format\HTMLFile; <?php namespace Todaymade\Daux\Format\HTMLFile;
use RuntimeException;
use Todaymade\Daux\Tree\Content; use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory; use Todaymade\Daux\Tree\Directory;
@ -21,9 +22,15 @@ class Book
return '<style>' . file_get_contents('themes/daux_singlepage/css/main.min.css') . '</style>'; 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) protected function buildNavigation(Directory $tree)
@ -37,7 +44,7 @@ class Book
$nav[] = [ $nav[] = [
'title' => $node->getTitle(), 'title' => $node->getTitle(),
'href' => '#' . $this->getPageUrl($node), 'href' => '#section_' . $this->getSectionId($node),
]; ];
} elseif ($node instanceof Directory) { } elseif ($node instanceof Directory) {
if (!$node->hasContent()) { if (!$node->hasContent()) {
@ -48,7 +55,7 @@ class Book
$nav[] = [ $nav[] = [
'title' => $node->getTitle(), 'title' => $node->getTitle(),
'href' => '#' . $this->getPageUrl($page_index), 'href' => '#section_' . $this->getSectionId($page_index),
'children' => $this->buildNavigation($node), 'children' => $this->buildNavigation($node),
]; ];
} }
@ -63,9 +70,9 @@ class Book
foreach ($entries as $entry) { foreach ($entries as $entry) {
if (array_key_exists('children', $entry)) { if (array_key_exists('children', $entry)) {
if (array_key_exists('href', $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 { } 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']); $link .= $this->renderNavigation($entry['children']);
@ -88,7 +95,7 @@ class Book
protected function generateCover() protected function generateCover()
{ {
return '<div>' . return "<div>" .
"<h1 style='font-size:40pt; margin-bottom:0;'>{$this->cover['title']}</h1>" . "<h1 style='font-size:40pt; margin-bottom:0;'>{$this->cover['title']}</h1>" .
"<p><strong>{$this->cover['subject']}</strong> by {$this->cover['author']}</p>" . "<p><strong>{$this->cover['subject']}</strong> by {$this->cover['author']}</p>" .
'</div><div class="PageBreak">&nbsp;</div>'; '</div><div class="PageBreak">&nbsp;</div>';
@ -97,8 +104,8 @@ class Book
protected function generatePages() protected function generatePages()
{ {
$content = ''; $content = '';
foreach ($this->pages as $page) { foreach ($this->pages as $section => $page) {
$content .= '<a id="' . $this->getPageUrl($page['page']) . '"></a>'; $content .= '<a id="section_' . $section . '"></a>';
$content .= '<h1>' . $page['page']->getTitle() . '</h1>'; $content .= '<h1>' . $page['page']->getTitle() . '</h1>';
$content .= '<section class="s-content">' . $page['content'] . '</section>'; $content .= '<section class="s-content">' . $page['content'] . '</section>';
$content .= '<div class="PageBreak">&nbsp;</div>'; $content .= '<div class="PageBreak">&nbsp;</div>';

View File

@ -11,23 +11,23 @@ class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
{ {
$content = parent::generatePage(); $content = parent::generatePage();
// Embed images //Embed images
// We do it after generation so we can catch the images that were in html already // 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( ->embed(
$content, $content,
$this->file, $this->file,
function ($src, array $attributes, Raw $file) { function($src, array $attributes, Raw $file) {
// TODO :: ignore absolute paths $content = base64_encode(file_get_contents($file->getPath()));
$content = base64_encode(file_get_contents($file->getPath()));
$attr = ''; $attr = '';
foreach ($attributes as $name => $value) { foreach ($attributes as $name => $value) {
$attr .= ' ' . $name . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"'; $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 "<img $attr src=\"data:image/png;base64,$content\"/>";
} }
); );
return $content;
} }
} }

View File

@ -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'));
}
}

View File

@ -1,9 +0,0 @@
<?php namespace Todaymade\Daux\Format\HTMLFile\ContentTypes\Markdown;
class ContentType extends \Todaymade\Daux\ContentTypes\Markdown\ContentType
{
protected function createConverter()
{
return new CommonMarkConverter(['daux' => $this->config]);
}
}

View File

@ -1,66 +0,0 @@
<?php namespace Todaymade\Daux\Format\HTMLFile\ContentTypes\Markdown;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement;
use League\CommonMark\Inline\Element\AbstractInline;
use League\CommonMark\Inline\Element\Link;
use Todaymade\Daux\DauxHelper;
use Todaymade\Daux\Exception\LinkNotFoundException;
class LinkRenderer extends \Todaymade\Daux\ContentTypes\Markdown\LinkRenderer
{
/**
* @param AbstractInline|Link $inline
*
* @throws LinkNotFoundException
*
* @return HtmlElement
*/
public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
{
if (!($inline instanceof Link)) {
throw new \InvalidArgumentException('Incompatible inline type: ' . \get_class($inline));
}
$element = parent::render($inline, $htmlRenderer);
$url = $inline->getUrl();
// empty urls and anchors should
// not go through the url resolver
if (!DauxHelper::isValidUrl($url)) {
return $element;
}
// Absolute urls, shouldn't either
if (DauxHelper::isExternalUrl($url)) {
$element->setAttribute('class', 'Link--external');
return $element;
}
// if there's a hash component in the url, we can directly use it as all pages are in the same file
$urlAndHash = explode('#', $url);
if (isset($urlAndHash[1])) {
$element->setAttribute('href', '#' . $urlAndHash[1]);
return $element;
}
try {
$file = DauxHelper::resolveInternalFile($this->daux, $url);
$url = $file->getUrl();
} catch (LinkNotFoundException $e) {
if ($this->daux->isStatic()) {
throw $e;
}
$element->setAttribute('class', 'Link--broken');
}
$url = str_replace('/', '_', $url);
$element->setAttribute('href', "#file_$url");
return $element;
}
}

View File

@ -4,28 +4,26 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Console\RunAction; use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\Daux; use Todaymade\Daux\Daux;
use Todaymade\Daux\Format\HTML\HTMLUtils;
use Todaymade\Daux\Format\HTML\Template; use Todaymade\Daux\Format\HTML\Template;
use Todaymade\Daux\Format\HTMLFile\ContentTypes\Markdown\ContentType; use Todaymade\Daux\Format\HTML\ContentTypes\Markdown\ContentType;
class Generator implements \Todaymade\Daux\Format\Base\Generator class Generator implements \Todaymade\Daux\Format\Base\Generator
{ {
use RunAction; use RunAction;
use HTMLUtils;
/** @var Daux */ /** @var Daux */
protected $daux; protected $daux;
/** @var Template */ /**
protected $templateRenderer; * @param Daux $daux
*/
public function __construct(Daux $daux) public function __construct(Daux $daux)
{ {
$config = $daux->getConfig(); $params = $daux->getParams();
$this->daux = $daux; $this->daux = $daux;
$this->templateRenderer = new Template($config); $this->templateRenderer = new Template($params);
$config->templateRenderer = $this->templateRenderer; $params->templateRenderer = $this->templateRenderer;
} }
/** /**
@ -34,35 +32,56 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
public function getContentTypes() public function getContentTypes()
{ {
return [ return [
'markdown' => new ContentType($this->daux->getConfig()), 'markdown' => new ContentType($this->daux->getParams()),
]; ];
} }
protected function initPDF()
{
// create new PDF document
$pdf = new Book(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$params = $this->daux->getParams();
// set document information
$pdf->SetCreator(PDF_CREATOR);
// set default header data
$pdf->SetHeaderData('', 0, $params['title'], $params['tagline']);
// set header and footer fonts
$pdf->setHeaderFont([PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN]);
$pdf->setFooterFont([PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA]);
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// set auto page breaks
$pdf->SetAutoPageBreak(true, PDF_MARGIN_BOTTOM);
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// set font
$pdf->SetFont('helvetica', '', 10);
return $pdf;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function generateAll(InputInterface $input, OutputInterface $output, $width) public function generateAll(InputInterface $input, OutputInterface $output, $width)
{ {
$destination = $input->getOption('destination'); $params = $this->daux->getParams();
$config = $this->daux->getConfig(); $data = ['author' => $params['author'], 'title' => $params['title'], 'subject' => $params['tagline']];
if (is_null($destination)) {
$destination = $config->getLocalBase() . DIRECTORY_SEPARATOR . 'static';
}
$this->runAction(
'Cleaning destination folder ...',
$width,
function () use ($destination) {
$this->ensureEmptyDestination($destination);
}
);
$data = [
'author' => $config->getAuthor(),
'title' => $config->getTitle(),
'subject' => $config->getTagline(),
];
$book = new Book($this->daux->tree, $data); $book = new Book($this->daux->tree, $data);
@ -71,9 +90,9 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator
$this->runAction( $this->runAction(
'Generating ' . $current->getTitle(), 'Generating ' . $current->getTitle(),
$width, $width,
function () use ($book, $current, $config) { function () use ($book, $current, $params) {
$contentType = $this->daux->getContentTypeHandler()->getType($current); $contentType = $this->daux->getContentTypeHandler()->getType($current);
$content = ContentPage::fromFile($current, $config, $contentType); $content = ContentPage::fromFile($current, $params, $contentType);
$content->templateRenderer = $this->templateRenderer; $content->templateRenderer = $this->templateRenderer;
$content = $content->getContent(); $content = $content->getContent();
$book->addPage($current, $content); $book->addPage($current, $content);

View File

@ -1,23 +0,0 @@
<?php namespace Todaymade\Daux;
use IntlDateFormatter;
class FormatDate
{
public static function format($config, $date)
{
$locale = $config->getLanguage();
$datetype = IntlDateFormatter::LONG;
$timetype = IntlDateFormatter::SHORT;
$timezone = null;
if (!extension_loaded('intl')) {
$locale = 'en';
$timezone = 'GMT';
}
$formatter = new IntlDateFormatter($locale, $datetype, $timetype, $timezone);
return $formatter->format($date);
}
}

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