├── wp-content ├── index.php ├── themes │ ├── index.php │ └── wuxt │ │ ├── cpts │ │ └── index.php │ │ ├── taxonomies │ │ └── index.php │ │ ├── screenshot.png │ │ ├── style.css │ │ ├── wuxt-logo.svg │ │ ├── api-extensions │ │ ├── relation.php │ │ ├── front-page.php │ │ ├── slug.php │ │ ├── meta.php │ │ └── menu.php │ │ ├── index.php │ │ └── functions.php └── mu-plugins │ ├── wuxt-headless-wp-api-extensions │ ├── languages │ │ └── wuxt-wp-rest-api-extensions.pot │ ├── screenshot-1.png │ ├── screenshot-2.png │ ├── screenshot-3.png │ ├── extensions │ │ ├── relation.php │ │ ├── menu.php │ │ ├── front-page.php │ │ ├── slug.php │ │ ├── meta.php │ │ ├── generate.php │ │ └── geo.php │ ├── wuxt-headless-wp-api-extensions.php │ ├── readme.txt │ └── classes │ │ └── menu-array.class.php │ └── load.php ├── _assets ├── wuxt.png └── wuxt-env.png ├── nuxt ├── static │ ├── icon.png │ ├── favicon.ico │ ├── wuxt-logo.svg │ └── README.md ├── .prettierrc ├── assets │ ├── styles │ │ └── main.scss │ └── README.md ├── modules │ ├── wp-api │ │ ├── routes │ │ │ ├── menu.js │ │ │ ├── slug.js │ │ │ ├── front-page.js │ │ │ └── cpt.js │ │ ├── index.js │ │ └── plugin.js │ └── static │ │ ├── plugin.js │ │ └── index.js ├── components │ ├── README.md │ ├── templates │ │ ├── Page.vue │ │ └── Post.vue │ └── Logo.vue ├── .editorconfig ├── layouts │ ├── README.md │ └── default.vue ├── pages │ ├── README.md │ ├── _single.vue │ └── index.vue ├── plugins │ ├── wp-api-docker-connector.js │ └── README.md ├── middleware │ └── README.md ├── store │ └── README.md ├── README.md ├── .eslintrc.js ├── server │ └── index.js ├── .gitignore ├── package.json └── nuxt.config.js ├── .gitignore ├── _scripts ├── tools │ ├── yarn.js │ ├── env.js │ └── wp.js ├── scaffold │ ├── cpt.js │ └── taxonomy.js └── _helpers.js ├── dist.yml ├── package.json ├── docker-compose.yml ├── CODE_OF_CONDUCT.md ├── yarn.lock ├── README.md └── LICENSE /wp-content/index.php: -------------------------------------------------------------------------------- 1 | 2 | (wp.menu = wp.registerRoute('wuxt/v1', '/menu', { 3 | params: ['location'] 4 | })) 5 | -------------------------------------------------------------------------------- /nuxt/modules/wp-api/routes/slug.js: -------------------------------------------------------------------------------- 1 | export default wp => 2 | (wp.slug = wp.registerRoute('wuxt/v1', '/slug/(?P)', { 3 | params: ['embed'] 4 | })) 5 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/load.php: -------------------------------------------------------------------------------- 1 | 2 | (wp.frontPage = wp.registerRoute('wuxt/v1', '/front-page', { 3 | params: ['embed'] 4 | })) 5 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/northosts/wuxt/HEAD/wp-content/mu-plugins/wuxt-headless-wp-api-extensions/screenshot-1.png -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/northosts/wuxt/HEAD/wp-content/mu-plugins/wuxt-headless-wp-api-extensions/screenshot-2.png -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/northosts/wuxt/HEAD/wp-content/mu-plugins/wuxt-headless-wp-api-extensions/screenshot-3.png -------------------------------------------------------------------------------- /nuxt/components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /nuxt/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /nuxt/modules/wp-api/routes/cpt.js: -------------------------------------------------------------------------------- 1 | export default wp => 2 | (wp.cpt = postType => { 3 | wp[postType] = wp.registerRoute('wp/v2', '/' + postType + '/(?P)', { 4 | params: ['embed', 'before', 'after', 'author', 'parent', 'post'] 5 | }) 6 | return wp[postType]() 7 | }) 8 | -------------------------------------------------------------------------------- /nuxt/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /nuxt/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /nuxt/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules/* 3 | _db 4 | wp-content/themes/twenty* 5 | wp-content/uploads/* 6 | wp-content/plugins/* 7 | wp-content/themes/wuxt/cpts/* 8 | !wp-content/themes/wuxt/cpts/index.php 9 | wp-content/themes/wuxt/taxonomies/* 10 | !wp-content/themes/wuxt/taxonomies/index.php 11 | .DS_Store 12 | nuxt/dist/* 13 | .env 14 | -------------------------------------------------------------------------------- /nuxt/plugins/wp-api-docker-connector.js: -------------------------------------------------------------------------------- 1 | export default function(context) { 2 | console.log('ENVS', process.env); 3 | if (process.env.NODE_ENV === 'development') { 4 | const { app } = context 5 | app.$wp._options.endpoint = 'http://localhost:' + (process.env.WUXT_PORT_BACKEND ? process.env.WUXT_PORT_BACKEND : '3080') + '/wp-json/' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nuxt/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /nuxt/modules/wp-api/index.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | 3 | const defaults = {} 4 | 5 | module.exports = function wp(moduleOptions) { 6 | const options = Object.assign({}, defaults, this.options.wp, moduleOptions) 7 | 8 | this.addPlugin({ 9 | src: resolve(__dirname, './plugin.js'), 10 | fileName: 'plugin.js', 11 | options 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /nuxt/components/templates/Page.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | -------------------------------------------------------------------------------- /nuxt/components/templates/Post.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | -------------------------------------------------------------------------------- /nuxt/static/wuxt-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /nuxt/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: wuxt 3 | Theme URI: https://wordpress.org/themes/wuxt/ 4 | Author: Northosts AB 5 | Author URI: https://northosts.se/ 6 | Description: Prepares the WordPress Rest API for optimal nuxt.js responses 7 | Version: 0.1 8 | License: GNU General Public License v2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | Text Domain: wuxt 11 | Tags: api, nuxt, wuxt 12 | */ 13 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/wuxt-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /nuxt/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /nuxt/README.md: -------------------------------------------------------------------------------- 1 | # wuxt 2 | 3 | > Initial wuxt app 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | $ yarn install 10 | 11 | # serve with hot reload at localhost:3000 12 | $ yarn run dev 13 | 14 | # build for production and launch server 15 | $ yarn run build 16 | $ yarn start 17 | 18 | # generate static project 19 | $ yarn run generate 20 | ``` 21 | 22 | For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org). 23 | -------------------------------------------------------------------------------- /nuxt/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /_scripts/tools/yarn.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | var spawn = require('child_process').spawn; 3 | var helpers = require('../_helpers'); 4 | 5 | var container = process.env.WUXT_NUXT_CONTAINER || 'front.wuxt'; 6 | 7 | helpers.checkContainers([container], function(containerRunning) { 8 | 9 | if (!containerRunning) { 10 | console.log('ERROR: ' + container + ' container is not running. Try "docker-compose up -d"') 11 | return; 12 | } 13 | 14 | helpers.runYarn(container, function() {}); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /nuxt/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /nuxt/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'plugin:nuxt/recommended', 13 | 'plugin:prettier/recommended', 14 | 'prettier', 15 | 'prettier/vue' 16 | ], 17 | plugins: ['prettier'], 18 | // add your custom rules here 19 | rules: { 20 | 'nuxt/no-cjs-in-config': 'off', 21 | 'no-console': 'off' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nuxt/pages/_single.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 28 | -------------------------------------------------------------------------------- /nuxt/modules/wp-api/plugin.js: -------------------------------------------------------------------------------- 1 | import WPApi from 'wpapi' 2 | 3 | /** 4 | * Routes 5 | */ 6 | import registerFrontPage from '~/modules/wp-api/routes/front-page' 7 | import registerMenu from '~/modules/wp-api/routes/menu' 8 | import registerSlug from '~/modules/wp-api/routes/slug' 9 | import registerCPT from '~/modules/wp-api/routes/cpt' 10 | 11 | const wp = new WPApi(<%= serialize(options) %>) 12 | 13 | export default (ctx, inject) => { 14 | /** 15 | * Register routes 16 | */ 17 | registerFrontPage(wp) 18 | registerMenu(wp) 19 | registerSlug(wp) 20 | registerCPT(wp) 21 | 22 | /** 23 | * Inject 24 | */ 25 | inject('wp', wp) 26 | } 27 | -------------------------------------------------------------------------------- /dist.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | dist.wuxt: 5 | container_name: dist.wuxt 6 | image: nginx:alpine 7 | working_dir: '/usr/share/nginx/html' 8 | environment: 9 | - HOST=0.0.0.0 10 | ports: 11 | - '${WUXT_PORT_DIST}:80' 12 | volumes: 13 | - ./nuxt/dist:/usr/share/nginx/html 14 | networks: 15 | - dist 16 | command: sh -c 'echo "server {listen 80;server_name localhost;location / {root /usr/share/nginx/html;index index.html index.htm;absolute_redirect off;}}" > /etc/nginx/conf.d/default.conf && nginx -g "daemon off;"' 17 | 18 | 19 | networks: 20 | dist: 21 | -------------------------------------------------------------------------------- /_scripts/tools/env.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | var spawn = require('child_process').spawn; 3 | var helpers = require('../_helpers'); 4 | 5 | helpers.changeEnv(function(err, project, portBackend, portFrontend, portDist) { 6 | 7 | console.log('Shutting down old containers ...'); 8 | helpers.dockerDown(function() { 9 | 10 | console.log(''); 11 | console.log('Run docker-compose up -d to start your new environment!'); 12 | console.log(''); 13 | console.log(' [project] ' + project); 14 | console.log(' [wp] http://localhost:' + portBackend); 15 | console.log(' [front] http://localhost:' + portFrontend); 16 | console.log(' [dist] http://localhost:' + portDist + ' (lookup static generation in the docs)'); 17 | 18 | }); 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/api-extensions/relation.php: -------------------------------------------------------------------------------- 1 | get( 'tax_query' ) ) ) { 17 | return; 18 | } 19 | 20 | foreach ( $tax_query as $index => $tax ) { 21 | $tax_query[$index]['operator'] = 'AND'; 22 | } 23 | 24 | $query->set( 'tax_query', $tax_query ); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /nuxt/server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const consola = require('consola') 3 | const { Nuxt, Builder } = require('nuxt') 4 | const app = express() 5 | 6 | // Import and Set Nuxt.js options 7 | const config = require('../nuxt.config.js') 8 | config.dev = !(process.env.NODE_ENV === 'production') 9 | 10 | async function start() { 11 | // Init Nuxt.js 12 | const nuxt = new Nuxt(config) 13 | 14 | const { host, port } = nuxt.options.server 15 | 16 | // Build only in dev mode 17 | if (config.dev) { 18 | const builder = new Builder(nuxt) 19 | await builder.build() 20 | } else { 21 | await nuxt.ready() 22 | } 23 | 24 | // Give nuxt middleware to express 25 | app.use(nuxt.render) 26 | 27 | // Listen the server 28 | app.listen(port, host) 29 | consola.ready({ 30 | message: `Server listening on http://${host}:${port}`, 31 | badge: true 32 | }) 33 | } 34 | start() 35 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/extensions/relation.php: -------------------------------------------------------------------------------- 1 | get( 'tax_query' ) ) ) { 17 | return; 18 | } 19 | 20 | foreach ( $tax_query as $index => $tax ) { 21 | $tax_query[$index]['operator'] = 'AND'; 22 | } 23 | 24 | $query->set( 'tax_query', $tax_query ); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /_scripts/tools/wp.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | var spawn = require('child_process').spawn; 3 | var helpers = require('../_helpers'); 4 | 5 | var container = process.env.WUXT_WP_CONTAINER || 'wp.wuxt'; 6 | 7 | helpers.checkContainers([container], function(containerRunning) { 8 | 9 | if (!containerRunning) { 10 | console.log('ERROR: ' + container + ' container is not running. Try "docker-compose up -d"') 11 | return; 12 | } 13 | 14 | 15 | helpers.checkWPCli(container, function(wpCliRunning) { 16 | if (!wpCliRunning) { 17 | 18 | console.log('WARNING: wp cli not installed, trying auto install ...'); 19 | 20 | helpers.installWPCli(container, function(wpCliRunning) { 21 | 22 | console.log('SUCCESS: wp cli installed!'); 23 | helpers.runWPCli(container, function() {}); 24 | 25 | }); 26 | } else { 27 | 28 | helpers.runWPCli(container, function() {}); 29 | 30 | } 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /_scripts/scaffold/cpt.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | var helpers = require('../_helpers'); 3 | 4 | var container = process.env.WUXT_WP_CONTAINER || 'wp.wuxt'; 5 | 6 | helpers.checkContainers([container], function(containerRunning) { 7 | 8 | if (!containerRunning) { 9 | console.log('ERROR: ' + container + ' container is not running. Try "docker-compose up -d"') 10 | return; 11 | } 12 | 13 | 14 | helpers.checkWPCli(container, function(wpCliRunning) { 15 | if (!wpCliRunning) { 16 | 17 | console.log('WARNING: wp cli not installed, trying auto install ...'); 18 | 19 | helpers.installWPCli(container, function(wpCliRunning) { 20 | 21 | console.log('SUCCESS: wp cli installed!'); 22 | helpers.generateWPCPT(container, function() { 23 | console.log('done!'); 24 | }); 25 | 26 | }); 27 | } else { 28 | 29 | helpers.generateWPCPT(container, function() { 30 | console.log('done!'); 31 | }); 32 | 33 | } 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /_scripts/scaffold/taxonomy.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | var helpers = require('../_helpers'); 3 | 4 | var container = process.env.WUXT_WP_CONTAINER || 'wp.wuxt'; 5 | 6 | helpers.checkContainers([container], function(containerRunning) { 7 | 8 | if (!containerRunning) { 9 | console.log('ERROR: ' + container + ' container is not running. Try "docker-compose up -d"') 10 | return; 11 | } 12 | 13 | helpers.checkWPCli(container, function(wpCliRunning) { 14 | if (!wpCliRunning) { 15 | 16 | console.log('WARNING: wp cli not installed, trying auto install ...'); 17 | 18 | helpers.installWPCli(container, function(wpCliRunning) { 19 | 20 | console.log('SUCCESS: wp cli installed!'); 21 | helpers.generateWPTax(container, function() { 22 | console.log('done!'); 23 | }); 24 | 25 | }); 26 | } else { 27 | 28 | helpers.generateWPTax(container, function() { 29 | console.log('done!'); 30 | }); 31 | 32 | } 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/api-extensions/front-page.php: -------------------------------------------------------------------------------- 1 | 'GET', 13 | 'callback' => 'wuxt_get_front_page' 14 | )); 15 | } 16 | 17 | 18 | function wuxt_get_front_page( $object ) { 19 | 20 | $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); 21 | 22 | $frontpage_id = get_option( 'page_on_front' ); 23 | if ( $frontpage_id ) { 24 | $request = new WP_REST_Request( 'GET', '/wp/v2/pages/' . $frontpage_id ); 25 | } 26 | 27 | $response = rest_do_request( $request ); 28 | if ($response->is_error()) { 29 | return new WP_Error( 'wuxt_request_error', __( 'Request Error' ), array( 'status' => 500 ) ); 30 | } 31 | 32 | $embed = $object->get_param( '_embed' ) !== NULL; 33 | $data = rest_get_server()->response_to_data( $response, $embed ); 34 | 35 | return $data; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/extensions/menu.php: -------------------------------------------------------------------------------- 1 | 'GET', 13 | 'callback' => 'wuxt_get_menu', 14 | )); 15 | } 16 | 17 | 18 | function wuxt_get_menu($params) 19 | { 20 | $params = $params->get_params(); 21 | $theme_locations = get_nav_menu_locations(); 22 | 23 | if (!isset($params['location'])) { 24 | $params['location'] = 'main'; 25 | } 26 | 27 | if (!isset($theme_locations[$params['location']])) { 28 | return new WP_Error('wuxt_menu_error', __('Menu location does not exist'), array('status' => 404)); 29 | } 30 | 31 | $menu_obj = get_term($theme_locations[$params['location']], 'nav_menu'); 32 | 33 | if (!$menu_obj) { 34 | return new WP_Error('wuxt_menu_error', __('Menu location does not exist'), array('status' => 404)); 35 | } 36 | 37 | $menu = new wuxt_Menu($params['location']); 38 | 39 | return $menu->getTree(); 40 | } 41 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/api-extensions/slug.php: -------------------------------------------------------------------------------- 1 | \S+)', array( 13 | 'methods' => 'GET', 14 | 'callback' => 'wuxt_get_slug' 15 | )); 16 | } 17 | 18 | 19 | function wuxt_get_slug($object) 20 | { 21 | 22 | $slug = $object->get_param('slug'); 23 | 24 | $request = new WP_REST_Request('GET', '/wp/v2/posts'); 25 | $request->set_param('slug', $slug); 26 | 27 | $response = rest_do_request($request); 28 | 29 | if (!$response->data) { 30 | 31 | $request = new WP_REST_Request('GET', '/wp/v2/pages'); 32 | $request->set_param('slug', $slug); 33 | 34 | $response = rest_do_request($request); 35 | } 36 | 37 | if (!$response->data) { 38 | return new WP_Error('wuxt_no_such_slug', __('Slug does not exist'), array('status' => 404)); 39 | } 40 | 41 | $embed = $object->get_param('_embed') !== NULL; 42 | $data = rest_get_server()->response_to_data($response, $embed); 43 | 44 | return $data[0]; 45 | } 46 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/extensions/front-page.php: -------------------------------------------------------------------------------- 1 | 'GET', 13 | 'callback' => 'wuxt_get_front_page' 14 | )); 15 | } 16 | 17 | 18 | function wuxt_get_front_page( $object ) { 19 | 20 | $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); 21 | 22 | $frontpage_id = get_option( 'page_on_front' ); 23 | if ( $frontpage_id ) { 24 | $request = new WP_REST_Request( 'GET', '/wp/v2/pages/' . $frontpage_id ); 25 | } 26 | 27 | $response = rest_do_request( $request ); 28 | if ($response->is_error()) { 29 | return new WP_Error( 'wuxt_request_error', __( 'Request Error' ), array( 'status' => 500 ) ); 30 | } 31 | 32 | $embed = $object->get_param( '_embed' ) !== NULL; 33 | $data = rest_get_server()->response_to_data( $response, $embed ); 34 | 35 | return $data; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /nuxt/modules/static/plugin.js: -------------------------------------------------------------------------------- 1 | import { getMatchedComponents } from './utils.js' 2 | import Middleware from './middleware' 3 | 4 | Middleware.nuxt_static = async ({ app, route }) => { 5 | // Ignore on server 6 | if (process.server) return 7 | // Ignore if not generated 8 | if (!process.static) return 9 | 10 | const Components = getMatchedComponents(route) 11 | Components.forEach(Component => { 12 | Component._payloads = Component._payloads || {} 13 | if (Component.options.asyncData) { 14 | Component.options.asyncData = ({ route }) => Component._payloads[route.path.replace(/\/$/, '')] 15 | } 16 | }) 17 | const path = route.path.replace(/\/$/, '') 18 | const needFetch = Components.some(Component => Component.options.asyncData && !Component._payloads[path]) 19 | if (!needFetch) { 20 | return 21 | } 22 | const payloadPath = (path + '/payload.json').replace(/\/+/, '/') 23 | const pageDatas = await fetch(payloadPath).then(res => { 24 | if (!res.ok) return null 25 | return res.json() 26 | }) 27 | if (!pageDatas) return console.error(`[@nuxt/static] Could not fetch ${payloadPath}`) 28 | 29 | Components.forEach((Component, index) => { 30 | if (Component.options.asyncData) { 31 | Component._payloads[path] = pageDatas[index] 32 | } 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/extensions/slug.php: -------------------------------------------------------------------------------- 1 | \S+)', array( 11 | 'methods' => 'GET', 12 | 'callback' => 'wuxt_get_slug' 13 | )); 14 | } 15 | 16 | 17 | function wuxt_get_slug($object){ 18 | 19 | $slug = $object->get_param('slug'); 20 | 21 | $request = new WP_REST_Request('GET', '/wp/v2/posts'); 22 | $request->set_param('slug', $slug); 23 | 24 | $response = rest_do_request($request); 25 | 26 | if (!$response->data) { 27 | 28 | $request = new WP_REST_Request('GET', '/wp/v2/pages'); 29 | $request->set_param('slug', $slug); 30 | 31 | $response = rest_do_request($request); 32 | } 33 | 34 | if (!$response->data) { 35 | return new WP_Error('wuxt_no_such_slug', __('Slug does not exist'), array('status' => 404)); 36 | } 37 | 38 | $embed = $object->get_param('_embed') !== NULL; 39 | $data = rest_get_server()->response_to_data($response, $embed); 40 | 41 | return $data[0]; 42 | } 43 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/wuxt-headless-wp-api-extensions.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | WUXTJS 9 | 10 | 49 | 50 | 51 | 52 |
53 | 54 |

55 | WUXT 56 | JS 57 | API 58 |

59 |
60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wuxt", 3 | "version": "0.0.1", 4 | "description": "WordPress and nuxt.js in an awesome dev environment", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "enter:front": "docker exec -ti front.wuxt bash", 9 | "enter:wp": "docker exec -ti wp.wuxt bash", 10 | "enter:mysql": "docker exec -ti mysql.wuxt bash", 11 | "wp": "node ./_scripts/tools/wp.js", 12 | "env": "node ./_scripts/tools/env.js", 13 | "yarn": "node ./_scripts/tools/yarn.js", 14 | "scaffold:cpt": "node ./_scripts/scaffold/cpt.js", 15 | "scaffold:tax": "node ./_scripts/scaffold/taxonomy.js", 16 | "generate": "docker exec front.wuxt yarn generate && docker-compose --log-level CRITICAL -f dist.yml up -d && docker-compose --log-level CRITICAL -f dist.yml restart && docker-compose --log-level CRITICAL down && docker-compose --log-level CRITICAL up -d" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/northosts/wuxt.git" 21 | }, 22 | "keywords": [ 23 | "nuxt.js", 24 | "nuxt", 25 | "wordpress", 26 | "wp", 27 | "wp-rest-api" 28 | ], 29 | "author": "Northosts AB", 30 | "license": "ISC", 31 | "bugs": { 32 | "url": "https://github.com/northosts/wuxt/issues" 33 | }, 34 | "homepage": "https://github.com/northosts/wuxt#readme", 35 | "devDependencies": { 36 | "mkdirp": "^0.5.1", 37 | "prompt": "^1.0.0", 38 | "slugify": "^1.3.4" 39 | }, 40 | "dependencies": { 41 | "cross-env": "^5.2.0", 42 | "dotenv": "^8.1.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | services: 3 | mysql.wuxt: 4 | container_name: ${WUXT_MYSQL_CONTAINER:-mysql.wuxt} 5 | image: mysql:5.7 6 | volumes: 7 | - ./_db:/var/lib/mysql 8 | environment: 9 | MYSQL_ROOT_PASSWORD: dev 10 | networks: 11 | - default 12 | 13 | wp.wuxt: 14 | container_name: ${WUXT_WP_CONTAINER:-wp.wuxt} 15 | image: wordpress:5.2.2-php7.2-apache 16 | volumes: 17 | - ./wp-content:/var/www/html/wp-content:rw,cached 18 | expose: 19 | - '${WUXT_PORT_BACKEND:-3080}' 20 | ports: 21 | - '${WUXT_PORT_BACKEND:-3080}:80' 22 | environment: 23 | WORDPRESS_DB_HOST: ${WUXT_MYSQL_CONTAINER:-mysql.wuxt}:3306 24 | WORDPRESS_DB_NAME: wuxt 25 | WORDPRESS_DB_USER: root 26 | WORDPRESS_DB_PASSWORD: dev 27 | WUXT_PORT_BACKEND: ${WUXT_PORT_BACKEND:-3080} 28 | WORDPRESS_CONFIG_EXTRA: | 29 | define('WP_SITEURL', 'http://localhost:${WUXT_PORT_BACKEND:-3080}' ); 30 | define('WP_HOME', 'http://localhost:${WUXT_PORT_BACKEND:-3080}' ); 31 | networks: 32 | - default 33 | 34 | front.wuxt: 35 | container_name: ${WUXT_NUXT_CONTAINER:-front.wuxt} 36 | image: node:11-slim 37 | working_dir: '/var/www/app' 38 | environment: 39 | - HOST=0.0.0.0 40 | - WUXT_WP_CONTAINER=${WUXT_WP_CONTAINER:-wp.wuxt} 41 | - WUXT_PORT_BACKEND=${WUXT_PORT_BACKEND:-3080} 42 | ports: 43 | - '${WUXT_PORT_FRONTEND-3000}:3000' 44 | volumes: 45 | - ./nuxt:/var/www/app 46 | command: bash -c "yarn install && WUXT_PORT_BACKEND=${WUXT_PORT_BACKEND:-3080} yarn dev" 47 | networks: 48 | - default 49 | 50 | networks: 51 | default: 52 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/api-extensions/meta.php: -------------------------------------------------------------------------------- 1 | true ) ); 17 | } 18 | } 19 | 20 | } 21 | 22 | } 23 | } 24 | 25 | } 26 | 27 | 28 | function wuxt_register_yoast_meta() { 29 | if(in_array('wordpress-seo/wp-seo.php', apply_filters('active_plugins', get_option('active_plugins')))){ 30 | 31 | $allowed_yoast_keywords = array( 32 | '_yoast_wpseo_title', 33 | '_yoast_wpseo_bctitle', 34 | '_yoast_wpseo_metadesc', 35 | '_yoast_wpseo_focuskw', 36 | '_yoast_wpseo_meta-robots-noindex', 37 | '_yoast_wpseo_meta-robots-nofollow', 38 | '_yoast_wpseo_meta-robots-adv', 39 | '_yoast_wpseo_canonical', 40 | '_yoast_wpseo_redirect', 41 | '_yoast_wpseo_opengraph-description', 42 | ); 43 | 44 | foreach( $allowed_yoast_keywords as $field) { 45 | register_meta( 'post', $field, array( 'show_in_rest' => true ) ); 46 | } 47 | 48 | } 49 | } 50 | 51 | add_action( 'init', 'wuxt_register_acf_meta' ); 52 | add_action( 'init', 'wuxt_register_yoast_meta' ); 53 | -------------------------------------------------------------------------------- /nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wuxt", 3 | "version": "1.0.0", 4 | "description": "Initial wuxt app", 5 | "author": "Northosts AB", 6 | "private": true, 7 | "scripts": { 8 | "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server", 9 | "build": "nuxt build", 10 | "start": "cross-env NODE_ENV=production node server/index.js", 11 | "generate": "nuxt generate", 12 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", 13 | "precommit": "npm run lint" 14 | }, 15 | "dependencies": { 16 | "@nuxtjs/axios": "^5.6.0", 17 | "@nuxtjs/pwa": "^2.6.0", 18 | "axios": "^0.19.0", 19 | "cross-env": "^5.2.0", 20 | "express": "^4.17.1", 21 | "fstream": "^1.0.12", 22 | "lodash": "^4.17.15", 23 | "lodash.template": "^4.5.0", 24 | "nuxt": "2.9.2", 25 | "nuxt-webfontloader": "^1.1.0", 26 | "wpapi": "^1.2.1" 27 | }, 28 | "devDependencies": { 29 | "@nuxtjs/eslint-config": "^1.1.2", 30 | "babel-eslint": "^10.0.3", 31 | "coffee-loader": "^0.9.0", 32 | "coffeescript": "^2.4.1", 33 | "eslint": "^5.15.1", 34 | "eslint-config-prettier": "^6.1.0", 35 | "eslint-config-standard": ">=12.0.0", 36 | "eslint-loader": "^3.0.0", 37 | "eslint-plugin-import": ">=2.18.2", 38 | "eslint-plugin-jest": ">=22.15.1", 39 | "eslint-plugin-node": ">=9.1.0", 40 | "eslint-plugin-nuxt": ">=0.4.2", 41 | "eslint-plugin-prettier": "^3.1.0", 42 | "eslint-plugin-promise": ">=4.2.1", 43 | "eslint-plugin-standard": ">=4.0.1", 44 | "eslint-plugin-vue": "^5.2.3", 45 | "node-sass": "^4.12.0", 46 | "nodemon": "^1.19.1", 47 | "prettier": "^1.18.2", 48 | "pug": "2.0.4", 49 | "pug-plain-loader": "^1.0.0", 50 | "sass-loader": "^7.3.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/functions.php: -------------------------------------------------------------------------------- 1 | true ) ); 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | 26 | 27 | function wuxt_register_yoast_meta() { 28 | if(in_array('wordpress-seo/wp-seo.php', apply_filters('active_plugins', get_option('active_plugins')))){ 29 | 30 | $allowed_yoast_keywords = array( 31 | '_yoast_wpseo_title', 32 | '_yoast_wpseo_bctitle', 33 | '_yoast_wpseo_metadesc', 34 | '_yoast_wpseo_focuskw', 35 | '_yoast_wpseo_meta-robots-noindex', 36 | '_yoast_wpseo_meta-robots-nofollow', 37 | '_yoast_wpseo_meta-robots-adv', 38 | '_yoast_wpseo_canonical', 39 | '_yoast_wpseo_redirect', 40 | '_yoast_wpseo_opengraph-description', 41 | ); 42 | 43 | foreach( $allowed_yoast_keywords as $field) { 44 | register_meta( 'post', $field, array( 'show_in_rest' => true ) ); 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 37 | 38 | 72 | -------------------------------------------------------------------------------- /nuxt/modules/static/index.js: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/DreaMinder/nuxt-payload-extractor 2 | // Credits to @DreMinder 3 | const path = require('path') 4 | const { writeFile, ensureDir } = require('fs-extra') 5 | 6 | let extractPayload = function({ html, route }, windowNamespace){ 7 | let chunks = html.split(`').shift() 10 | let post = chunks[1].split('').slice(1).join('') 11 | let path = route === '/' ? '' : route 12 | 13 | return { 14 | html: pre + '' + post, 15 | payload 16 | } 17 | } 18 | 19 | let writePayload = async function (payload, dir, windowNamespace){ 20 | // Make sure the directory exists 21 | await ensureDir(dir) 22 | 23 | // Write payload.js file 24 | await writeFile(path.resolve(dir, 'payload.js'), `window.${windowNamespace}=${payload}`, 'utf-8') 25 | 26 | // if routes are nested, ignore parent routs and extract last one 27 | const nuxtContext = eval('('+payload+')') 28 | const pageData = nuxtContext.data 29 | 30 | // Write payload.json (page data) 31 | await writeFile(path.resolve(dir, 'payload.json'), JSON.stringify(pageData), 'utf-8') 32 | } 33 | 34 | module.exports = function (moduleOptions) { 35 | const options = { 36 | blacklist: [], 37 | ...this.options.static, 38 | ...moduleOptions 39 | } 40 | 41 | this.nuxt.hook('generate:page', async page => { 42 | if (!this.nuxt.options.generate.subFolders) { 43 | throw new Error('generate.subFolders should be true for @nuxt/static') 44 | } 45 | if (options.blacklist.includes(page.route)) { 46 | return page 47 | } 48 | 49 | const windowNamespace = this.nuxt.options.globals.context(this.nuxt.options.globalName) 50 | let { html, payload } = extractPayload(page, windowNamespace) 51 | 52 | await writePayload(payload, path.join(this.nuxt.options.generate.dir, page.route), windowNamespace) 53 | page.html = html 54 | 55 | return page 56 | }) 57 | 58 | // Add nuxt_static middleware 59 | this.addPlugin({ 60 | src: path.resolve(__dirname, 'plugin.js') 61 | }) 62 | this.nuxt.options.router.middleware.push('nuxt_static') 63 | } 64 | -------------------------------------------------------------------------------- /nuxt/nuxt.config.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const pkg = require('./package') 3 | 4 | module.exports = { 5 | mode: 'universal', 6 | 7 | env: { 8 | WUXT_PORT_BACKEND: process.env.WUXT_PORT_BACKEND || '3080' 9 | }, 10 | 11 | /* 12 | ** Headers of the page 13 | */ 14 | head: { 15 | title: pkg.name, 16 | meta: [ 17 | { charset: 'utf-8' }, 18 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 19 | { hid: 'description', name: 'description', content: pkg.description } 20 | ], 21 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }] 22 | }, 23 | 24 | /* 25 | ** Customize the progress-bar color 26 | */ 27 | loading: { color: '#333' }, 28 | 29 | /* 30 | ** Global CSS 31 | */ 32 | css: ['@/assets/styles/main.scss'], 33 | 34 | /* 35 | ** Plugins to load before mounting the App 36 | */ 37 | plugins: [{ src: '~/plugins/wp-api-docker-connector', ssr: false }], 38 | 39 | /* 40 | ** Nuxt.js modules 41 | */ 42 | modules: [ 43 | // Doc: https://axios.nuxtjs.org/usage 44 | '@nuxtjs/axios', 45 | '@nuxtjs/pwa', 46 | '~/modules/static/', 47 | [ 48 | '~/modules/wp-api/index', 49 | { 50 | endpoint: 'http://' + (process.env.WUXT_WP_CONTAINER ? process.env.WUXT_WP_CONTAINER : 'wp.wuxt') + ':80/wp-json/' 51 | } 52 | ] 53 | ], 54 | /* 55 | ** Axios module configuration 56 | */ 57 | axios: { 58 | // See https://github.com/nuxt-community/axios-module#options 59 | }, 60 | 61 | /* 62 | ** Build configuration 63 | */ 64 | build: { 65 | /* 66 | ** You can extend webpack config here 67 | */ 68 | // extend(config, ctx) { 69 | // // Run ESLint on save 70 | // if (ctx.isDev && ctx.isClient) { 71 | // config.module.rules.push({ 72 | // enforce: 'pre', 73 | // test: /\.(js|vue)$/, 74 | // loader: 'eslint-loader', 75 | // exclude: /(node_modules)/ 76 | // }) 77 | // } 78 | // } 79 | }, 80 | 81 | generate: { 82 | routes: function() { 83 | return axios 84 | .get('http://' + (process.env.WUXT_WP_CONTAINER ? process.env.WUXT_WP_CONTAINER : 'wp.wuxt') + ':80/wp-json/wuxt/v1/generate') 85 | .then(({ data }) => data) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /nuxt/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 24 | 25 | 26 | 123 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/extensions/generate.php: -------------------------------------------------------------------------------- 1 | 'GET', 13 | 'callback' => 'wuxt_get_generate_urls' 14 | )); 15 | } 16 | 17 | 18 | function wuxt_get_generate_urls( $object ) { 19 | 20 | $site_url = get_site_url(); 21 | 22 | $published_posts = new WP_Query( array( 23 | 'post_type' => 'any', 24 | 'posts_per_page' => -1, 25 | 'post_status' => 'publish' 26 | ) ); 27 | 28 | $urls = array(); 29 | 30 | // published posts 31 | foreach( $published_posts->posts as $post ) { 32 | switch ( $post->post_type ) { 33 | case 'post': 34 | $urls[] = str_replace( $site_url, '', get_permalink( $post->ID ) ); 35 | break; 36 | case 'page': 37 | $urls[] = str_replace( $site_url, '', get_page_link( $post->ID ) ); 38 | break; 39 | case 'attachment': 40 | $urls[] = str_replace( $site_url, '', get_attachment_link( $post->ID ) ); 41 | break; 42 | case 'nav_menu_item': 43 | $urls[] = str_replace( $site_url, '', get_post_meta( $post->ID, '_menu_item_url', true ) ); 44 | break; 45 | case 'revision': break; 46 | default: 47 | $urls[] = str_replace( $site_url, '', get_post_permalink( $post->ID ) ); 48 | break; 49 | } 50 | } 51 | 52 | $menus = wp_get_nav_menus(); 53 | $menu_locations = get_nav_menu_locations(); 54 | foreach ( $menus as $menu ) { 55 | foreach ( wp_get_nav_menu_items( $menu ) as $item ) { 56 | 57 | // check if intern link 58 | if ( substr( $item->url, 0, strlen( $site_url ) ) === $site_url ) { 59 | $urls[] = str_replace( $site_url, '', $item->url ); 60 | } 61 | 62 | } 63 | } 64 | 65 | return array_values( array_unique( $urls ) ); 66 | 67 | } 68 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at daniel@northosts.se. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/extensions/geo.php: -------------------------------------------------------------------------------- 1 | 500 ) ); 27 | } 28 | 29 | $lat_meta_key = ( $matches[1] ) ? sanitize_key( $matches[1] ) : 'lat'; 30 | $lng_meta_key = ( $matches[3] ) ? sanitize_key( $matches[3] ) : 'lng'; 31 | $lat = floatVal( $matches[2] ); 32 | $lng = floatVal( $matches[4] ); 33 | 34 | preg_match( '/([0-9]+\.?[0-9]*)(.*)/', $_GET['distance'], $matches ); 35 | if ( count( $matches ) != 3 ) { 36 | return new WP_Error( 'wuxt_geo_error', __( 'The distance parameter couldn\'t be parsed' ), array( 'status' => 500 ) ); 37 | } 38 | 39 | $unit = ( $matches[2] == 'km' || $matches[2] == 'm' ) ? $matches[2] : 'km'; 40 | $distance = floatVal( $matches[1] ); 41 | 42 | $geo_query = array( 43 | 'lat' => $lat, 44 | 'lng' => $lng, 45 | 'distance' => $distance, 46 | 'unit' => $unit, 47 | 'lat_key' => $lat_meta_key, 48 | 'lng_key' => $lng_meta_key, 49 | ); 50 | 51 | $wp_query->set( 'geo', $geo_query ); 52 | $wp_query->set( 'orderby', 'distance' ); 53 | $wp_query->set( 'order', 'ASC' ); 54 | 55 | } 56 | 57 | 58 | function wuxt_haversine( $geo_query ) { 59 | global $wpdb; 60 | 61 | $radius = 6371; 62 | if ( ! empty( $geo_query['unit'] ) && 'm' == $geo_query['unit'] ) { 63 | $radius = 3959; 64 | } 65 | 66 | $haversine = '( ' . $radius . ' * ' . 67 | 'acos( cos( radians(%f) ) * cos( radians( geo_lat.meta_value ) ) * ' . 68 | 'cos( radians( geo_lng.meta_value ) - radians(%f) ) + ' . 69 | 'sin( radians(%f) ) * sin( radians( geo_lat.meta_value ) ) ) ' . 70 | ')'; 71 | 72 | return $wpdb->prepare( $haversine, array( $geo_query['lat'], $geo_query['lng'], $geo_query['lat'] ) ); 73 | } 74 | 75 | 76 | function wuxt_posts_fields( $sql_query, $wp_query ) { 77 | 78 | global $wpdb; 79 | 80 | $geo_query = $wp_query->get( 'geo' ); 81 | 82 | if ( $geo_query ) { 83 | $sql_query .= ', ' . wuxt_haversine( $geo_query ) . ' AS geo_distance'; 84 | } 85 | 86 | return $sql_query; 87 | 88 | } 89 | 90 | 91 | function wuxt_posts_join( $sql_query, $wp_query ) { 92 | 93 | global $wpdb; 94 | 95 | $geo_query = $wp_query->get( 'geo' ); 96 | 97 | if ( $geo_query ) { 98 | $sql_query .= ' INNER JOIN ' . $wpdb->prefix . 'postmeta AS geo_lat ON ( ' . $wpdb->prefix . 'posts.ID = geo_lat.post_id ) ' . 99 | 'INNER JOIN ' . $wpdb->prefix . 'postmeta AS geo_lng ON ( ' . $wpdb->prefix . 'posts.ID = geo_lng.post_id ) '; 100 | } 101 | 102 | return $sql_query; 103 | 104 | } 105 | 106 | 107 | function wuxt_posts_where( $sql_query, $wp_query ) { 108 | 109 | global $wpdb; 110 | 111 | $geo_query = $wp_query->get( 'geo' ); 112 | 113 | if ( $geo_query ) { 114 | 115 | $haversine = wuxt_haversine( $geo_query ); 116 | 117 | $sql = ' AND ( geo_lat.meta_key = %s AND geo_lng.meta_key = %s AND ' . $haversine . ' <= %f )'; 118 | $sql_query .= $wpdb->prepare( $sql, $geo_query['lat_key'], $geo_query['lng_key'], $geo_query['distance'] ); 119 | 120 | } 121 | 122 | return $sql_query; 123 | 124 | } 125 | 126 | 127 | function wuxt_posts_orderby( $sql_query, $wp_query ) { 128 | 129 | $geo_query = $wp_query->get( 'geo' ); 130 | 131 | if ( $geo_query && 'distance' == $wp_query->get( 'orderby' ) ) { 132 | $sql_query = 'geo_distance ASC'; 133 | } 134 | 135 | return $sql_query; 136 | 137 | } 138 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/readme.txt: -------------------------------------------------------------------------------- 1 | === Plugin Name === 2 | Contributors: danielauener 3 | Donate link: http://www.danielauener.com/ 4 | Tags: rest api, endpoint extension, headless 5 | Requires at least: 4.7.0 6 | Tested up to: 5.2.2 7 | Stable tag: 1.0 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | Extensions for the Rest API to provide endpoints that support a more convenient 12 | use of headless WordPress as back-end CMS. 13 | 14 | == Description == 15 | 16 | This plugin adds a couple of extensions to the WordPress Rest API, which are aimed to make the use of WordPress as headless CMS easier. 17 | It is originally coded for [WUXT](https://github.com/northosts/wuxt), a dockerized development environment for headless WordPress combined with NuxtJs. However, it can be used by every other application, which needs a powerful headless WordPress back-end. 18 | 19 | === WordPress API Extensions === 20 | 21 | * **Frontpage endpoint**: There is no obvious way to get the WordPress front-page via the Rest API. To read the settings, you have to be authorized, which makes things unnecessary complicated. The new endpoint returns the front-page object if it is set, the ten newest posts otherwise. 22 | * **Menu endpoint**: Right now, there is no way I know of, for getting menus from the API. This endpoint returns an entire menu as nested array. Default location is "main", but you can request other locations. 23 | * **Slug endpoint**: If you are building a front-end app on top of WordPress, you have to think about how to structure your urls. WordPress has two default post-types (posts & pages) and in the urls is not distinguished which type you are requesting, so http://wp-site.expl/something might lead to a page or a post, dependent on the type of the object with the slug something. If you want to mirror that behaviour in your app, you have to do two requests for each url, one searching pages, one searching posts. To make that one request, use the slug end-point. 24 | * **Taxonomy filter AND extension**: When filtering taxonomies with an Rest API request, all queries are OR-queries. That means you can get posts which are either in category A or B. Our adjustment lets you switch all tax_queries to an AND-relation, so that you can select posts which are both in category A and B. 25 | * **Geo query**: If your application has to get posts by geographical proximity, you can use a geo query. 26 | * **WordPress SEO meta fields**: They are included automatically in the meta object if the Yoast WordPress SEO plugin is activated. 27 | * **Advanced custom fields** are included automatically in the meta object if the plugin is activated. 28 | 29 | === Endpoints and parameters === 30 | 31 | **Frontpage** 32 | 33 | * GET /wp-json/wuxt/v1/front-page 34 | * GET /wp-json/wuxt/v1/front-page?_embed 35 | 36 | **Menu** 37 | 38 | * GET /wp-json/wuxt/v1/menu 39 | * GET /wp-json/wuxt/v1/menu?location=<location> 40 | 41 | **Slug** 42 | 43 | * GET /wp-json/wuxt/v1/slug/<post-or-page-slug> 44 | * GET /wp-json/wuxt/v1/slug/<post-or-page-slug>?_embed 45 | 46 | **Taxonomy filter AND extension** 47 | 48 | * GET /wp-json/wp/v2/posts/?categories=1,2&and=true 49 | 50 | **GEO query** 51 | 52 | * GET /wp-json/wp/v2/posts/?coordinates=<lat>,<lng>&distance=<distance><km|m> 53 | * GET /wp-json/wp/v2/posts/?coordinates=<lat_meta_field>:<lat>,<lng_meta_field>:<lng>&distance=<distance><km|m> 54 | * GET /wp-json/wp/v2/posts/?coordinates=52.585,13.373&distance=10 55 | * GET /wp-json/wp/v2/posts/?coordinates=lat_mkey:52.585,lng_mkey:13.373&distance=10 56 | * GET /wp-json/wp/v2/posts/?coordinates=52.585,13.373&distance=10m 57 | 58 | == Installation == 59 | 60 | 1. Upload the `wuxt-headless-wp-api-extensions` folder to the `/wp-content/plugins/` directory 61 | 2. Activate the plugin through the 'Plugins' menu in WordPress 62 | 3. Use the new endpoints 63 | 5. done 64 | 65 | == Frequently Asked Questions == 66 | 67 | == Screenshots == 68 | 69 | 1. Posts request extended with meta-fields from ACF and Yoast WordPress SEO 70 | 2. New menu endpoint 71 | 3. New front-page endpoint 72 | 73 | == Links == 74 | 75 | * [More detailed end-point description](https://www.danielauener.com/wordpress-rest-api-extensions-for-going-headless-wp/) 76 | * [WUXT](https://github.com/northosts/wuxt) 77 | * [WUXT release blog post](https://www.danielauener.com/nuxt-js-wordpress-wuxt/) 78 | * [NuxtJs](https://nuxtjs.org/) 79 | 80 | == Credits == 81 | 82 | * Michael Cox [Menu Class for returning a menu as array](https://gist.github.com/michaeland/191ce08d22fed74da05a) 83 | 84 | == Changelog == 85 | 86 | = 1.0 = 87 | * Version 1.0 done 88 | -------------------------------------------------------------------------------- /wp-content/mu-plugins/wuxt-headless-wp-api-extensions/classes/menu-array.class.php: -------------------------------------------------------------------------------- 1 | getTree(); 6 | * $menuItems = $mainMenu->getMenuItems(); 7 | */ 8 | class wuxt_Menu { 9 | protected $menu; 10 | protected $tree; 11 | 12 | 13 | /** 14 | * Initialises $this->menu 15 | */ 16 | public function __construct($menuName = '', $args = array(), $filter = null) { 17 | 18 | $filter = is_callable($filter) ? $filter : null; 19 | if (empty($menuName)) { 20 | throw new Exception('No menu location name provided.'); 21 | return; 22 | } 23 | $menuLocations = get_nav_menu_locations(); 24 | 25 | if (empty($menuLocations[$menuName])) return; 26 | 27 | $this->menu = $this->retrieveMenu(wp_get_nav_menu_object($menuLocations[$menuName]), $args, $filter); 28 | } 29 | 30 | 31 | /** 32 | * Retrieves menu from wordpress 33 | * @uses private core function _wp_menu_item_classes_by_context() 34 | * @return Array|null $this->menu 35 | */ 36 | protected function retrieveMenu($navMenuObject = null, $args = array(), $filter = null) { 37 | global $wp_query; 38 | global $post; 39 | 40 | $queriedPostType = get_post_type(); 41 | $this->queriedPostType = $queriedPostType; 42 | $isTax = (is_tax() || is_tag()); 43 | if (!$navMenuObject) return null; 44 | 45 | $menu_items = wp_get_nav_menu_items( $navMenuObject->term_id , $args ); 46 | 47 | /* the following was taken from wp_nav_menu core function 48 | * line 154–169: https://developer.wordpress.org/reference/functions/wp_nav_menu/ 49 | */ 50 | // set up menu item classes 51 | _wp_menu_item_classes_by_context($menu_items); 52 | $sorted_menu_items = $menu_items_with_children = array(); 53 | foreach ( (array) $menu_items as $menu_item ) { 54 | $sorted_menu_items[ $menu_item->menu_order ] = $menu_item; 55 | if ( $menu_item->menu_item_parent ) 56 | $menu_items_with_children[ $menu_item->menu_item_parent ] = true; 57 | } 58 | 59 | // Add the menu-item-has-children class where applicable 60 | if ( $menu_items_with_children ) { 61 | foreach ( $sorted_menu_items as &$menu_item ) { 62 | if ( isset( $menu_items_with_children[ $menu_item->ID ] ) ) 63 | $menu_item->classes[] = 'menu-item-has-children'; 64 | } 65 | } 66 | 67 | 68 | // end taken wp_nav_menu from core function 69 | foreach($menu_items as $key => &$menu_item) { 70 | // Add isCurrent class to parents or ancestors 71 | $menu_item->isCurrent = false; 72 | if ($menu_item->current_item_ancestor) $menu_item->isCurrent = 'ancestor'; 73 | if ($menu_item->current_item_parent && ($menu_item->type !== 'taxonomy')) { 74 | $menu_item->isCurrent = 'parent ancestor'; 75 | } 76 | 77 | if ($menu_item->current) $menu_item->isCurrent = 'current'; 78 | 79 | // Add isCurrent class, and ancestor classes to custom post type archive menu items 80 | $isCurrentCustomPostType = static::menuItemIsCustomPostTypeArchive($menu_item, $queriedPostType); 81 | 82 | // if is current 83 | if ($isCurrentCustomPostType) { 84 | $menu_item->classes[] = "current-{$queriedPostType}-ancestor"; 85 | $menu_item->isCurrent = 'ancestor'; 86 | } 87 | 88 | // if is current and !hierarchical 89 | if ($isCurrentCustomPostType && !is_post_type_hierarchical($queriedPostType)) { 90 | $menu_item->classes[] = "current-{$queriedPostType}-parent"; 91 | $menu_item->isCurrent = 'parent ancestor'; 92 | } 93 | 94 | // if is current and hierarchical and top level 95 | if ($post 96 | && $isCurrentCustomPostType 97 | && is_post_type_hierarchical($queriedPostType) 98 | && (count(get_post_ancestors($post->ID)) === 0) 99 | ) { 100 | $menu_item->classes[] = "current-{$queriedPostType}-parent"; 101 | $menu_item->isCurrent = 'parent ancestor'; 102 | } 103 | 104 | // if a filter exists, run it 105 | if ($filter && is_callable($filter)) { 106 | $filtered = $filter($menu_item); 107 | $menu_item = $filtered ? $filtered : null; 108 | if (!$menu_item) unset($menu_items[$key]); 109 | } 110 | } 111 | 112 | return $this->menu = $menu_items; 113 | } 114 | 115 | 116 | /** 117 | * Returns $this->menu 118 | */ 119 | public function getMenuItems() { 120 | return $this->menu; 121 | } 122 | 123 | 124 | /** 125 | * Returns $this->tree 126 | * 127 | * @return Array|null $tree 128 | */ 129 | public function getTree() { 130 | if ($this->tree !== null) return $this->tree; 131 | $tree = static::buildTree($this->menu, 0, 1); 132 | return $tree ? $tree : null; 133 | } 134 | 135 | 136 | /** 137 | * Transform a navigational menu to a tree structure 138 | * 139 | * @return Array $branch 140 | */ 141 | public static function buildTree(array &$elements, $parentId = 0, $level = 1) { 142 | $branch = array(); 143 | foreach ( $elements as &$element ) { 144 | if ( $element->menu_item_parent == $parentId ) { 145 | $subLevel = $level + 1; 146 | $element->level = $level; 147 | $children = static::buildTree( $elements, $element->ID, $subLevel ); 148 | 149 | if ($children) { 150 | $element->children = $children; 151 | } else { 152 | $element->children = array(); 153 | } 154 | $branch[$element->ID] = $element; 155 | unset($element); 156 | } 157 | } 158 | return $branch; 159 | } 160 | 161 | 162 | /** 163 | * Checks if menu item is a custom post type archive 164 | * 165 | * @return Boolean 166 | */ 167 | protected static function menuItemIsCustomPostTypeArchive($menuItem, $type = null) { 168 | $isCustomPostType = ( 169 | isset($menuItem->type) 170 | && ($menuItem->type === 'post_type_archive') 171 | && (is_post_type_archive($this->queriedPostType) || is_singular($this->queriedPostType)) 172 | ); 173 | if (!$type) { 174 | return $isCustomPostType; 175 | } 176 | $isOfType = ( 177 | $isCustomPostType 178 | && isset($menuItem->object) 179 | && ($menuItem->object === $type) 180 | ); 181 | 182 | return $isOfType; 183 | } 184 | 185 | 186 | /** 187 | * Returns maximum depth of menu tree 188 | * 189 | * @return Integer 190 | */ 191 | public static function menuItemDepth($menuItem = null) { 192 | $maxDepth = 0; 193 | foreach ($menuItem->children as $child) { 194 | if (is_array($child->children)) { 195 | $depth = static::menuItemDepth($child) + 1; 196 | if ($depth > $maxDepth) $maxDepth = $depth; 197 | } 198 | } 199 | return $maxDepth; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /wp-content/themes/wuxt/api-extensions/menu.php: -------------------------------------------------------------------------------- 1 | menu 15 | */ 16 | public function __construct($menuName = '', $args = array(), $filter = null) 17 | { 18 | $filter = is_callable($filter) ? $filter : null; 19 | if (empty($menuName)) { 20 | throw new Exception('No menu location name provided.'); 21 | return; 22 | } 23 | // $menuLocations = get_nav_menu_locations(); 24 | // if (empty($menuLocations[$menuName])) return; 25 | $this->menu = $this->retrieveMenu(wp_get_nav_menu_object($menuName), $args, $filter); 26 | } 27 | 28 | /** 29 | * Retrieves menu from wordpress 30 | * @uses private core function _wp_menu_item_classes_by_context() 31 | * @return Array|null $this->menu 32 | */ 33 | protected function retrieveMenu($navMenuObject = null, $args = array(), $filter = null) 34 | { 35 | global $wp_query; 36 | global $post; 37 | $queriedPostType = get_post_type(); 38 | $this->queriedPostType = $queriedPostType; 39 | $isTax = (is_tax() || is_tag()); 40 | if (!$navMenuObject) return null; 41 | $menu_items = wp_get_nav_menu_items($navMenuObject->term_id, $args); 42 | /* the following was taken from wp_nav_menu core function 43 | * line 154–169: https://developer.wordpress.org/reference/functions/wp_nav_menu/ 44 | */ 45 | // set up menu item classes 46 | _wp_menu_item_classes_by_context($menu_items); 47 | $sorted_menu_items = $menu_items_with_children = array(); 48 | foreach ((array)$menu_items as $menu_item) { 49 | $post = get_post($menu_item->object_id); 50 | 51 | if ($post) $menu_item->slug = $post->post_name === 'hem' ? '' : $post->post_name; 52 | 53 | $sorted_menu_items[$menu_item->menu_order] = $menu_item; 54 | if ($menu_item->menu_item_parent) 55 | $menu_items_with_children[$menu_item->menu_item_parent] = true; 56 | } 57 | 58 | // Add the menu-item-has-children class where applicable 59 | if ($menu_items_with_children) { 60 | foreach ($sorted_menu_items as &$menu_item) { 61 | if (isset($menu_items_with_children[$menu_item->ID])) 62 | $menu_item->classes[] = 'menu-item-has-children'; 63 | } 64 | } 65 | /* 66 | * end taken wp_nav_menu from core function 67 | */ 68 | foreach ($menu_items as $key => &$menu_item) { 69 | // Add isCurrent class to parents or ancestors 70 | $menu_item->isCurrent = false; 71 | if ($menu_item->current_item_ancestor) $menu_item->isCurrent = 'ancestor'; 72 | if ($menu_item->current_item_parent && ($menu_item->type !== 'taxonomy')) { 73 | $menu_item->isCurrent = 'parent ancestor'; 74 | } 75 | if ($menu_item->current) $menu_item->isCurrent = 'current'; 76 | // Add isCurrent class, and ancestor classes to custom post type archive menu items 77 | // $isCurrentCustomPostType = static::menuItemIsCustomPostTypeArchive($menu_item, $queriedPostType); 78 | // // if is current 79 | // if ($isCurrentCustomPostType) { 80 | // $menu_item->classes[] = "current-{$queriedPostType}-ancestor"; 81 | // $menu_item->isCurrent = 'ancestor'; 82 | // } 83 | // if is current and !hierarchical 84 | if ($isCurrentCustomPostType && !is_post_type_hierarchical($queriedPostType)) { 85 | $menu_item->classes[] = "current-{$queriedPostType}-parent"; 86 | $menu_item->isCurrent = 'parent ancestor'; 87 | } 88 | // if is current and hierarchical and top level 89 | if ( 90 | $post 91 | && $isCurrentCustomPostType 92 | && is_post_type_hierarchical($queriedPostType) 93 | && (count(get_post_ancestors($post->ID)) === 0) 94 | ) { 95 | $menu_item->classes[] = "current-{$queriedPostType}-parent"; 96 | $menu_item->isCurrent = 'parent ancestor'; 97 | } 98 | // if a filter exists, run it 99 | if ($filter && is_callable($filter)) { 100 | $filtered = $filter($menu_item); 101 | $menu_item = $filtered ? $filtered : null; 102 | if (!$menu_item) unset($menu_items[$key]); 103 | } 104 | } 105 | return $this->menu = $menu_items; 106 | } 107 | /** 108 | * Returns $this->menu 109 | */ 110 | public function getMenuItems() 111 | { 112 | return $this->menu; 113 | } 114 | /** 115 | * Returns $this->tree 116 | * 117 | * @return Array|null $tree 118 | */ 119 | public function getTree() 120 | { 121 | if ($this->tree !== null) return $this->tree; 122 | $tree = static::buildTree($this->menu, 0, 1); 123 | return $tree ? $tree : null; 124 | } 125 | /** 126 | * Transform a navigational menu to a tree structure 127 | * 128 | * @return Array $branch 129 | */ 130 | public static function buildTree(array &$elements, $parentId = 0, $level = 1) 131 | { 132 | $branch = array(); 133 | foreach ($elements as &$element) { 134 | if ($element->menu_item_parent == $parentId) { 135 | $subLevel = $level + 1; 136 | $element->level = $level; 137 | $children = static::buildTree($elements, $element->ID, $subLevel); 138 | if ($children) { 139 | $element->children = $children; 140 | } else { 141 | $element->children = array(); 142 | } 143 | $branch[$element->ID] = $element; 144 | unset($element); 145 | } 146 | } 147 | return $branch; 148 | } 149 | /** 150 | * Checks if menu item is a custom post type archive 151 | * 152 | * @return Boolean 153 | */ 154 | protected static function menuItemIsCustomPostTypeArchive($menuItem, $type = null) 155 | { 156 | $isCustomPostType = (isset($menuItem->type) 157 | && ($menuItem->type === 'post_type_archive') 158 | && (is_post_type_archive($this->queriedPostType) || is_singular($this->queriedPostType))); 159 | if (!$type) { 160 | return $isCustomPostType; 161 | } 162 | $isOfType = ($isCustomPostType 163 | && isset($menuItem->object) 164 | && ($menuItem->object === $type)); 165 | return $isOfType; 166 | } 167 | /** 168 | * Returns maximum depth of menu tree 169 | * 170 | * @return Integer 171 | */ 172 | public static function menuItemDepth($menuItem = null) 173 | { 174 | $maxDepth = 0; 175 | foreach ($menuItem->children as $child) { 176 | if (is_array($child->children)) { 177 | $depth = static::menuItemDepth($child) + 1; 178 | if ($depth > $maxDepth) $maxDepth = $depth; 179 | } 180 | } 181 | return $maxDepth; 182 | } 183 | } 184 | 185 | 186 | /** 187 | * Adds a menu endpoint 188 | */ 189 | 190 | add_action('init', 'wuxt_register_menu'); 191 | function wuxt_register_menu() 192 | { 193 | register_nav_menu('main', __('Main meny')); 194 | register_nav_menu('social', __('Social meny')); 195 | } 196 | 197 | 198 | add_action('rest_api_init', 'wuxt_route_menu'); 199 | function wuxt_route_menu() 200 | { 201 | register_rest_route('wuxt', '/v1/menu', array( 202 | 'methods' => 'GET', 203 | 'callback' => 'wuxt_get_menu', 204 | )); 205 | } 206 | 207 | 208 | function wuxt_get_menu($params) 209 | { 210 | $params = $params->get_params(); 211 | $theme_locations = get_nav_menu_locations(); 212 | 213 | if (!isset($params['location'])) { 214 | $params['location'] = 'main'; 215 | } 216 | 217 | if (!isset($theme_locations[$params['location']])) { 218 | return new WP_Error( 'wuxt_menu_error', __( 'Menu location does not exist' ), array( 'status' => 404 ) ); 219 | } 220 | 221 | $menu_obj = get_term($theme_locations[$params['location']], 'nav_menu'); 222 | 223 | $menu_name = $menu_obj->name; 224 | 225 | $menu = new Menu($menu_name); 226 | 227 | return $menu->getTree(); 228 | } 229 | -------------------------------------------------------------------------------- /_scripts/_helpers.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | /** 4 | * Check if wuxt containers are running 5 | */ 6 | checkContainers: function(names, done) { 7 | 8 | var exec = require('child_process').exec; 9 | exec('docker ps --format {{.Names}}', function(error, stdout, stderr) { 10 | done(names.map(function(name) { 11 | return stdout.split("\n").indexOf(name) >= 0; 12 | }).reduce(function(running, next) { 13 | return running && next; 14 | }, true)); 15 | }); 16 | 17 | }, 18 | 19 | /** 20 | * Check if wp cli is inastalled 21 | */ 22 | checkWPCli: function(container, done) { 23 | 24 | var exec = require('child_process').exec; 25 | exec('docker exec ' + container + ' bash -c \'wp\'', function(error, stdout, stderr) {}).on('exit', function(code) { 26 | done(127 !== code); 27 | }); 28 | 29 | }, 30 | 31 | /** 32 | * Check if wp cli is inastalled 33 | */ 34 | installWPCli: function(container, done) { 35 | 36 | var exec = require('child_process').exec; 37 | exec('docker exec ' + container + ' bash -c \'apt-get update && apt-get install -y less && curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp && wp --allow-root cli\'', function(error, stdout, stderr) {}).on('exit', function(code) { 38 | done(0 === code); 39 | }); 40 | 41 | }, 42 | 43 | /** 44 | * Run a wp command in the given container 45 | */ 46 | runWPCli: function(container, done) { 47 | 48 | var command = process.argv.slice(2); 49 | 50 | if (!command.length) { 51 | console.log('ERROR: Provide a valid wp-cli command!'); 52 | return done(); 53 | } 54 | 55 | var exec = require('child_process').exec; 56 | exec('docker exec ' + container + ' bash -c \'wp --allow-root ' + command.join(' ') + '\'', function(error, stdout, stderr) { 57 | console.log(stdout); 58 | }).on('exit', function(code) { 59 | done(); 60 | }); 61 | 62 | }, 63 | 64 | /** 65 | * Run docker-compose down 66 | */ 67 | dockerDown: function(done) { 68 | 69 | var exec = require('child_process').exec; 70 | exec('docker-compose down', function(error, stdout, stderr) { 71 | console.log(stdout); 72 | }).on('exit', function(code) { 73 | done(); 74 | }); 75 | 76 | 77 | }, 78 | 79 | /** 80 | * Run docker-compose down 81 | */ 82 | dockerUp: function(done) { 83 | 84 | var exec = require('child_process').exec; 85 | 86 | exec('cat .env', function(error, stdout, stderr) { 87 | console.log(stdout); 88 | }).on('exit', function(code) { 89 | 90 | exec('docker-compose up --force-recreate -d', function(error, stdout, stderr) { 91 | console.log(stdout); 92 | }).on('exit', function(code) { 93 | done(); 94 | }); 95 | 96 | }); 97 | 98 | }, 99 | 100 | /** 101 | * Run a yarn command in the given container 102 | */ 103 | runYarn: function(container, done) { 104 | 105 | var command = process.argv.slice(2); 106 | 107 | if (!command.length) { 108 | console.log('ERROR: Provide a valid yarn command!'); 109 | return done(); 110 | } 111 | 112 | var exec = require('child_process').exec; 113 | exec('docker exec ' + container + ' bash -c \'yarn ' + command.join(' ') + '\'', function(error, stdout, stderr) { 114 | console.log(stdout); 115 | }).on('exit', function(code) { 116 | done(); 117 | }); 118 | 119 | }, 120 | 121 | /** 122 | * Get Arguments 123 | */ 124 | getArgs: function(prompts, done) { 125 | 126 | if (Array.isArray(prompts)) { 127 | var result = {}; 128 | var i = 2; 129 | prompts.forEach(function(prompt) { 130 | if (process.argv[i] && prompt.pattern.test(process.argv[i])) { 131 | result[prompt.name] = process.argv[i]; 132 | } else { 133 | result = false; 134 | } 135 | i++; 136 | }); 137 | 138 | if (result) { 139 | return done(null, result); 140 | } 141 | } 142 | 143 | var prompt = require('prompt'); 144 | prompt.start(); 145 | 146 | prompt.get(prompts, function(err, result) { 147 | if (err) { 148 | return done('Something wrong with your input!', null); 149 | } 150 | 151 | done(null, result); 152 | 153 | }); 154 | 155 | }, 156 | 157 | /** 158 | * Change the environment to be able to run multiple wuxt installations 159 | */ 160 | changeEnv: function(done) { 161 | 162 | var fs = require('fs'); 163 | 164 | this.getArgs([{ 165 | name: 'project', 166 | description: 'Enter the project name', 167 | type: 'string', 168 | default: 'wuxt', 169 | message: 'Name must be a word' 170 | },{ 171 | name: 'port-front', 172 | description: 'Enter the port for nuxt front-end', 173 | type: 'number', 174 | default: '3000', 175 | message: 'Name must be a number' 176 | },{ 177 | name: 'port-wp', 178 | description: 'Enter the port for WordPress back-end', 179 | type: 'number', 180 | default: '3080', 181 | message: 'Name must be a number' 182 | },{ 183 | name: 'port-dist', 184 | description: 'Enter the port for server with generated html files', 185 | type: 'number', 186 | default: '8080', 187 | message: 'Name must be a number' 188 | }], function(err, result) { 189 | 190 | if (err) { 191 | console.log(err); 192 | return done(err, null, null, null, null); 193 | } 194 | 195 | var env = "WUXT_PORT_FRONTEND=" + result['port-front'] + "\n" + 196 | "WUXT_PORT_BACKEND=" + result['port-wp'] + "\n" + 197 | "WUXT_PORT_DIST=" + result['port-dist'] + "\n" + 198 | "WUXT_MYSQL_CONTAINER=mysql." + result['project'] + "\n" + 199 | "WUXT_WP_CONTAINER=wp." + result['project'] + "\n" + 200 | "WUXT_NUXT_CONTAINER=front." + result['project'] + "\n"; 201 | 202 | fs.writeFile('.env', env, function(err) { 203 | if (err) { 204 | console.log('ERROR: Could not create environment (' + err + ').') 205 | return done(err, null, null, null, null); 206 | } 207 | 208 | console.log('SUCCESS: New environment created.'); 209 | 210 | done(null, result['project'], result['port-wp'], result['port-front'], result['port-dist']); 211 | 212 | }); 213 | 214 | }); 215 | }, 216 | 217 | /** 218 | * Generate custom post type 219 | */ 220 | generateWPCPT: function(container, done) { 221 | 222 | var slugify = require('slugify'); 223 | var exec = require('child_process').exec; 224 | var fs = require('fs'); 225 | var mkdirp = require('mkdirp'); 226 | 227 | this.getArgs([{ 228 | name: 'name', 229 | description: 'Enter your post types name, e.g. Movie', 230 | type: 'string', 231 | pattern: /^\w+$/, 232 | message: 'Name must be a word' 233 | }], function(err, result) { 234 | 235 | if (err) { 236 | console.log(err); 237 | return done(); 238 | } 239 | 240 | var slug = slugify(result.name, { lower: true }); 241 | 242 | exec('docker exec ' + container + ' bash -c \'wp --allow-root scaffold post-type ' + slug + ' --label=' + result.name + ' --textdomain=wuxt\'', function(error, stdout, stderr) { 243 | if (!error) { 244 | 245 | mkdirp('./wp-content/themes/wuxt/cpts', function (err) { 246 | if (err) { 247 | console.log('ERROR: Could not create post-type (' + err + ').') 248 | return; 249 | } 250 | 251 | stdout = stdout.replace('\'title\', \'editor\'', '\'title\', \'editor\', \'custom-fields\'') 252 | 253 | fs.writeFile('./wp-content/themes/wuxt/cpts/' + slug + '.php', stdout, function(err) { 254 | if (err) { 255 | console.log('ERROR: Could not create post-type (' + err + ').') 256 | return; 257 | } 258 | 259 | console.log('SUCCESS: Post type ' + result.name + ' created.'); 260 | }); 261 | }); 262 | } else { 263 | console.log('ERROR: Could not create post-type (' + error + ').') 264 | } 265 | }).on('exit', function(code) { 266 | done(); 267 | }); 268 | 269 | }); 270 | }, 271 | 272 | /** 273 | * Generate custom post type 274 | */ 275 | generateWPTax: function(container, done) { 276 | 277 | var slugify = require('slugify'); 278 | var exec = require('child_process').exec; 279 | var fs = require('fs'); 280 | var mkdirp = require('mkdirp'); 281 | 282 | this.getArgs([{ 283 | name: 'name', 284 | description: 'Enter your taxonomies name, e.g. Venue', 285 | type: 'string', 286 | pattern: /^\w+$/, 287 | message: 'Name must be a word' 288 | },{ 289 | name: 'cpts', 290 | description: 'Enter a commaseparated list of post types for the taxonomy, e.g. event,presentation', 291 | type: 'string', 292 | pattern: /^\w+(,\w+)*$/, 293 | message: 'CPTs must be a list of post type slugs' 294 | }], function(err, result) { 295 | 296 | if (err) { 297 | console.log(err); 298 | return done(); 299 | } 300 | 301 | var slug = slugify(result.name, { lower: true }); 302 | 303 | exec('docker exec ' + container + ' bash -c \'wp --allow-root scaffold taxonomy ' + slug + ' --label=' + result.name + ' --post_types=' + result.cpts + ' --textdomain=wuxt\'', function(error, stdout, stderr) { 304 | if (!error) { 305 | 306 | mkdirp('./wp-content/themes/wuxt/taxonomies', function (err) { 307 | if (err) { 308 | console.log('ERROR: Could not create taxonomy (' + err + ').') 309 | return; 310 | } 311 | 312 | fs.writeFile('./wp-content/themes/wuxt/taxonomies/' + slug + '.php', stdout, function(err) { 313 | if (err) { 314 | console.log('ERROR: Could not create taxonomy (' + err + ').') 315 | return; 316 | } 317 | 318 | console.log('SUCCESS: Taxonomy ' + result.name + ' created.'); 319 | }); 320 | }); 321 | } else { 322 | console.log('ERROR: Could not create taxonomy (' + error + ').') 323 | } 324 | }).on('exit', function(code) { 325 | done(); 326 | }); 327 | 328 | }); 329 | } 330 | 331 | }; 332 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | async@~0.9.0: 6 | version "0.9.2" 7 | resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" 8 | integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= 9 | 10 | async@~1.0.0: 11 | version "1.0.0" 12 | resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" 13 | integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k= 14 | 15 | balanced-match@^1.0.0: 16 | version "1.0.0" 17 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 18 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 19 | 20 | brace-expansion@^1.1.7: 21 | version "1.1.11" 22 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 23 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 24 | dependencies: 25 | balanced-match "^1.0.0" 26 | concat-map "0.0.1" 27 | 28 | colors@1.0.x: 29 | version "1.0.3" 30 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 31 | integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= 32 | 33 | colors@^1.1.2: 34 | version "1.3.3" 35 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" 36 | integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== 37 | 38 | concat-map@0.0.1: 39 | version "0.0.1" 40 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 41 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 42 | 43 | cross-env@^5.2.0: 44 | version "5.2.0" 45 | resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2" 46 | integrity sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg== 47 | dependencies: 48 | cross-spawn "^6.0.5" 49 | is-windows "^1.0.0" 50 | 51 | cross-spawn@^6.0.5: 52 | version "6.0.5" 53 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 54 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== 55 | dependencies: 56 | nice-try "^1.0.4" 57 | path-key "^2.0.1" 58 | semver "^5.5.0" 59 | shebang-command "^1.2.0" 60 | which "^1.2.9" 61 | 62 | cycle@1.0.x: 63 | version "1.0.3" 64 | resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" 65 | integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= 66 | 67 | deep-equal@~0.2.1: 68 | version "0.2.2" 69 | resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d" 70 | integrity sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0= 71 | 72 | dotenv@^8.1.0: 73 | version "8.1.0" 74 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2" 75 | integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA== 76 | 77 | eyes@0.1.x: 78 | version "0.1.8" 79 | resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" 80 | integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= 81 | 82 | fs.realpath@^1.0.0: 83 | version "1.0.0" 84 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 85 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 86 | 87 | glob@^7.1.3: 88 | version "7.1.4" 89 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 90 | integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== 91 | dependencies: 92 | fs.realpath "^1.0.0" 93 | inflight "^1.0.4" 94 | inherits "2" 95 | minimatch "^3.0.4" 96 | once "^1.3.0" 97 | path-is-absolute "^1.0.0" 98 | 99 | i@0.3.x: 100 | version "0.3.6" 101 | resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d" 102 | integrity sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0= 103 | 104 | inflight@^1.0.4: 105 | version "1.0.6" 106 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 107 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 108 | dependencies: 109 | once "^1.3.0" 110 | wrappy "1" 111 | 112 | inherits@2: 113 | version "2.0.4" 114 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 115 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 116 | 117 | is-windows@^1.0.0: 118 | version "1.0.2" 119 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" 120 | integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== 121 | 122 | isexe@^2.0.0: 123 | version "2.0.0" 124 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 125 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 126 | 127 | isstream@0.1.x: 128 | version "0.1.2" 129 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 130 | integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= 131 | 132 | minimatch@^3.0.4: 133 | version "3.0.4" 134 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 135 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 136 | dependencies: 137 | brace-expansion "^1.1.7" 138 | 139 | minimist@0.0.8: 140 | version "0.0.8" 141 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 142 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 143 | 144 | mkdirp@0.x.x, mkdirp@^0.5.1: 145 | version "0.5.1" 146 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 147 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 148 | dependencies: 149 | minimist "0.0.8" 150 | 151 | mute-stream@~0.0.4: 152 | version "0.0.8" 153 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" 154 | integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== 155 | 156 | ncp@1.0.x: 157 | version "1.0.1" 158 | resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246" 159 | integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY= 160 | 161 | nice-try@^1.0.4: 162 | version "1.0.5" 163 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 164 | integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== 165 | 166 | once@^1.3.0: 167 | version "1.4.0" 168 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 169 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 170 | dependencies: 171 | wrappy "1" 172 | 173 | path-is-absolute@^1.0.0: 174 | version "1.0.1" 175 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 176 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 177 | 178 | path-key@^2.0.1: 179 | version "2.0.1" 180 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 181 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 182 | 183 | pkginfo@0.3.x: 184 | version "0.3.1" 185 | resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" 186 | integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE= 187 | 188 | pkginfo@0.x.x: 189 | version "0.4.1" 190 | resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" 191 | integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= 192 | 193 | prompt@^1.0.0: 194 | version "1.0.0" 195 | resolved "https://registry.yarnpkg.com/prompt/-/prompt-1.0.0.tgz#8e57123c396ab988897fb327fd3aedc3e735e4fe" 196 | integrity sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4= 197 | dependencies: 198 | colors "^1.1.2" 199 | pkginfo "0.x.x" 200 | read "1.0.x" 201 | revalidator "0.1.x" 202 | utile "0.3.x" 203 | winston "2.1.x" 204 | 205 | read@1.0.x: 206 | version "1.0.7" 207 | resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" 208 | integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= 209 | dependencies: 210 | mute-stream "~0.0.4" 211 | 212 | revalidator@0.1.x: 213 | version "0.1.8" 214 | resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" 215 | integrity sha1-/s5hv6DBtSoga9axgZgYS91SOjs= 216 | 217 | rimraf@2.x.x: 218 | version "2.7.1" 219 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 220 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 221 | dependencies: 222 | glob "^7.1.3" 223 | 224 | semver@^5.5.0: 225 | version "5.7.1" 226 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 227 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 228 | 229 | shebang-command@^1.2.0: 230 | version "1.2.0" 231 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 232 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 233 | dependencies: 234 | shebang-regex "^1.0.0" 235 | 236 | shebang-regex@^1.0.0: 237 | version "1.0.0" 238 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 239 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 240 | 241 | slugify@^1.3.4: 242 | version "1.3.4" 243 | resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb" 244 | integrity sha512-KP0ZYk5hJNBS8/eIjGkFDCzGQIoZ1mnfQRYS5WM3273z+fxGWXeN0fkwf2ebEweydv9tioZIHGZKoF21U07/nw== 245 | 246 | stack-trace@0.0.x: 247 | version "0.0.10" 248 | resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" 249 | integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= 250 | 251 | utile@0.3.x: 252 | version "0.3.0" 253 | resolved "https://registry.yarnpkg.com/utile/-/utile-0.3.0.tgz#1352c340eb820e4d8ddba039a4fbfaa32ed4ef3a" 254 | integrity sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo= 255 | dependencies: 256 | async "~0.9.0" 257 | deep-equal "~0.2.1" 258 | i "0.3.x" 259 | mkdirp "0.x.x" 260 | ncp "1.0.x" 261 | rimraf "2.x.x" 262 | 263 | which@^1.2.9: 264 | version "1.3.1" 265 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 266 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 267 | dependencies: 268 | isexe "^2.0.0" 269 | 270 | winston@2.1.x: 271 | version "2.1.1" 272 | resolved "https://registry.yarnpkg.com/winston/-/winston-2.1.1.tgz#3c9349d196207fd1bdff9d4bc43ef72510e3a12e" 273 | integrity sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4= 274 | dependencies: 275 | async "~1.0.0" 276 | colors "1.0.x" 277 | cycle "1.0.x" 278 | eyes "0.1.x" 279 | isstream "0.1.x" 280 | pkginfo "0.3.x" 281 | stack-trace "0.0.x" 282 | 283 | wrappy@1: 284 | version "1.0.2" 285 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 286 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 287 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Wuxt logo](_assets/wuxt.png?raw=true "Wuxt") 2 | 3 | # Nuxt/WordPress development environment - Wuxt 4 | 5 | **Wuxt** combines **_WordPress_**, the worlds biggest CMS with **_nuxt.js_**, 6 | the most awesome front-end application framework yet. 7 | 8 | - [Quick start](#quick) 9 | - [Introduction](#intro) 10 | - [Architecture](#env) 11 | - [Getting started](#start) 12 | - [Custom container configuration](#start-custom) 13 | - [Setup WordPress](#setup-wp) 14 | - [Setup Nuxt.js](#setup-nuxt) 15 | - [Generate and Deploy](#deploy) 16 | - [WordPress Rest API endpoints](#ep) 17 | - [Extensions to the API endpoints](#epp) 18 | - [Front-page](#epp-front) 19 | - [Menus](#epp-menu) 20 | - [Slugs](#epp-slugs) 21 | - [Meta queries](#epp-meta) 22 | - [Taxonomy queries](#epp-tax) 23 | - [Geo queries](#epp-geo) 24 | - [Custom post types](#epp-cpt) 25 | - [Scripts](#scripts) 26 | - [Working with the containers](#scripts-containers) 27 | - [WP-CLI and yarn](#scripts-containers-tools) 28 | - [Scaffolding](#scripts-scaffolding) 29 | - [Links](#links) 30 | - [Credits](#cred) 31 | 32 | ## Quick start 33 | 34 | 35 | git clone https://github.com/northosts/wuxt.git 36 | cd wuxt 37 | docker-compose up -d 38 | 39 | - [http://localhost:3080/install.php](http://localhost:3080/install.php) - Install WordPress 40 | - [http://localhost:3080/wp-admin/options-permalink.php](http://localhost:3080/wp-admin/options-permalink.php) - Set permalinks to *Post name* 41 | - [http://localhost:3080/wp-admin/themes.php](http://localhost:3080/wp-admin/themes.php) - Activate **wuxt**-theme 42 | - [http://localhost:3000](http://localhost:3000) - Done 43 | 44 | ## Introduction 45 | 46 | 47 | The goal of Wuxt is to provide a ready to use development environment, which makes the 48 | full power of **_WordPress_** easily available to your front-end app. Included 49 | in Wuxt are: 50 | 51 | - Fully dockerized **_WordPress_** and **_nuxt.js_** container configuration, 52 | `docker-compose up -d` sets up everything needed in one command and you can 53 | start working 54 | 55 | - Extended Rest API to give the front-end easy access to meta-fields, 56 | featured media menus or the front-page configuration. 57 | 58 | - The newest **_nuxt.js_** version, extended with a WordPress `$wp` object, to 59 | connect to the extended **_WordPress_** Rest API. 60 | 61 | All together the **Wuxt** features get you started with your front-end with just 62 | one command, you just need to work with the intuitive WordPress admin interface 63 | and can skip all back-end coding. But if you know your way around 64 | **_WordPress_** you are able to easily extend the back-end as well. 65 | 66 | ## The WUXT architecture 67 | 68 | 69 | ![WUXT environment](_assets/wuxt-env.png?raw=true "WUXT environment") 70 | 71 | ## Getting started 72 | 73 | 74 | First clone this repository to a directory you want, then change to that 75 | directory and simply start your containers (you need to have a running 76 | **_Docker_** installation of course): 77 | 78 | docker-compose up -d 79 | 80 | That starts the following containers: 81 | 82 | - **_MySql_** (`mysql.wuxt`) Database for your **_WordPress_** installation. The data-folder 83 | of the database-container is mirrored to the \_db-folder of your host system, to 84 | keep the data persistent. 85 | 86 | - **_WordPress_** (`wp.wuxt`) on a **_Apache_** server with the newest **_PHP_** version and 87 | the **Wuxt** Rest API extension theme, **_ACF_** and other good-to-have plugins 88 | pre-installed. The wp-content directory of the **_WordPress_** directory is 89 | mirrored to the wp-content directory on your host. 90 | 91 | - **_nuxt.js_** (`front.wuxt`) started in development mode with file-monitoring and 92 | browser-sync and extended by a complete **_WordPress_** Rest API wrapper and a 93 | starter application, mimicing base functions of a **_WordPress_** theme. 94 | 95 | Your containers are available at 96 | 97 | - front-end: `http://localhost:3000` 98 | - back-end: `http://localhost:3080`, `http://localhost:3080/wp-admin` 99 | - database: `docker exec -ti mysql.wuxt bash` 100 | 101 | ### Starting with custom container configuration 102 | 103 | 104 | **wuxt** allows you to change the above setup to run multiple projects at the 105 | same time or to adjust to your own environment. To change ports and container 106 | names, add an `.env` file in the same directory of your `docker-compose.yml` 107 | file. You can adjust the following values: 108 | 109 | WUXT_PORT_FRONTEND=3000 110 | WUXT_PORT_BACKEND=3080 111 | WUXT_PORT_DIST=8080 112 | WUXT_MYSQL_CONTAINER=mysql.wuxt 113 | WUXT_WP_CONTAINER=wp.wuxt 114 | WUXT_NUXT_CONTAINER=front.wuxt 115 | 116 | After you created the file, run 117 | 118 | docker-compose down 119 | docker-compose up -d 120 | 121 | There is even a script that does everything to you : 122 | 123 | npm run env 124 | 125 | You will be asked for projectname, front-end-port, back-end-port and dist-port. 126 | The script creates an `.env` file like the folllowing and stops the old containers: 127 | 128 | WUXT_PORT_FRONTEND=4000 129 | WUXT_PORT_BACKEND=4080 130 | WUXT_PORT_DIST=9080 131 | WUXT_MYSQL_CONTAINER=mysql.projectname 132 | WUXT_WP_CONTAINER=wp.projectname 133 | WUXT_NUXT_CONTAINER=front.projectname 134 | 135 | After running the script start the new containers 136 | 137 | docker-compose up -d 138 | 139 | ### Setup **_WordPress_** 140 | 141 | 142 | In short: 143 | 144 | - Install WordPress (`http://localhost:3080/install.php`) 145 | - Set permalinks to *Post name* (`http://localhost:3080/wp-admin/options-permalink.php`) 146 | - Activate **wuxt**-theme (`http://localhost:3080/wp-admin/themes.php`) 147 | 148 | Do a common **_WordPress_** installation at 149 | `http://localhost:3080/install.php`, then login to wp-admin and select the 150 | **wuxt** theme to activate all the API extensions. Additionally you might want 151 | to activate the **_ACF_** plugin to make your meta-value work easier. Last but 152 | not least you have to set the permalink structure to "Post Name" in the 153 | **_WordPress_** settings. 154 | 155 | To check if everything is running, visit `http://localhost:3080` and verify 156 | that the **wuxt** info screen is showing. 157 | 158 | Then check that the Rest API at `http://localhost:3080/wp-json` is returning 159 | a JSON-object you are good to go. 160 | 161 | ### Setup **_nuxt.js_** 162 | 163 | 164 | Nuxt should have been started automatically inside the docker container. The 165 | command we use for running the **_nuxt.js_** server is `yarn dev`. Check 166 | if the front-end is running by opening `http://localhost:3000`. You should 167 | be greeted by the **Wuxt** intro-screen. 168 | 169 | Check if **_BrowserSync_** is running, by doing a minor change to the 170 | front-page. The change should directly be visible on the front-page without manually reloading the page. 171 | 172 | ## Generate and Deploy 173 | 174 | 175 | To make a complete deploy of WUXT, you have to get at least one server and solve 176 | a lot of configuration stuff to get **_WordPress_**, **_MySql_** and **_nuxt.js_** 177 | running (we are working on a manual for some of the big cloud services). 178 | 179 | However, there is an easier solution, at least without on-site user generated 180 | content, like **_WordPress_**-comments (**_disqus_** would be ok, though). We 181 | have tweaked the **_nuxt.js_** *generate*-command, so that you can generate a 182 | fully static site with all your content, posts and pages inside the `dist` 183 | directory of **_nuxt_**. Then it's only a matter of getting the static html-site 184 | uploaded to a webspace of your choice. 185 | 186 | ### Generating a fully static site 187 | 188 | First be sure your containers are running 189 | 190 | docker-compose up -d 191 | 192 | Then go to the wuxt root-directory and run *generate* with yarn 193 | 194 | yarn generate 195 | 196 | Despite generating your static site, this commands runs some `docker-compose` 197 | action, so while generating, your WUXT site will be down and started again 198 | after. 199 | 200 | After the generation there will be started a small local web-server, which deploys 201 | the static site on 202 | 203 | http://localhost:8080 204 | 205 | To get the files you go to the `dist` directory inside your `wuxt/nuxt` directory: 206 | 207 | wuxt/nuxt/dist 208 | 209 | To shut down the local web-server you have to run the following command insie the 210 | `wuxt` directory: 211 | 212 | docker-compose -f dist.yml down 213 | 214 | ### How it works 215 | 216 | The *generate*-command simply scrapes all available urls you added to your 217 | **_nuxt.js_** and **_WordPress_** installation, saves the html-output and caches 218 | the JSON-requests to the **_WordPress_** Rest API. 219 | 220 | To know which urls are available, the generate command asks the **_WordPress_** 221 | Rest API for a list of existing endpoints and all links used in the 222 | **_WordPress_** menus. You can view that list with the following endpoint: 223 | 224 | GET: /wp-json/wuxt/v1/generate 225 | 226 | Since **_nuxt.js_** doesn't fully support 100% static sites yet, we have to get 227 | help of the `static` plugin used on **_nuxt.org_**, which is jsut taking care 228 | of the payload caching. Read more [here](https://github.com/nuxt/rfcs/issues/22) 229 | and [here](https://github.com/nuxt/nuxtjs.org/tree/master/modules/static). 230 | 231 | 232 | ## WordPress Rest API endpoints 233 | 234 | 235 | The **_WordPress_** Rest API gives you access to a wide range of native 236 | endpoints. Find the docs at: [https://developer.wordpress.org/rest-api/reference/](https://developer.wordpress.org/rest-api/reference/). To easily access the 237 | endpoints from **_nuxt.js_** you can use the `$wp` extension, which integrates 238 | the [node-wpapi](https://www.npmjs.com/package/node-wp) library. You can find the full documentation [here](https://github.com/WP-API/node-wpapi). 239 | 240 | ### Extensions to the API endpoints 241 | 242 | 243 | To make **wuxt** even more easy to use, there are a bunch of endpoint extensions to the **_WordPress_** Rest API. 244 | 245 | #### Front-page 246 | 247 | 248 | ``` 249 | $wp.frontPage() 250 | $wp.frontPage().embed() 251 | ``` 252 | 253 | or 254 | 255 | ``` 256 | GET: /wp-json/wuxt/v1/front-page 257 | GET: /wp-json/wuxt/v1/front-page?_embed 258 | ``` 259 | 260 | You can use the **_WordPress_** front-page settings to build your front-ends 261 | first page. If you setup the front-page in **_WordPress_** as static page, the 262 | endpoint will return the corresponing page object. 263 | 264 | If there is no front-page configured, the query automatically returns the 265 | result of the default posts query 266 | 267 | `GET` `/wp-json/wp/v2/posts` 268 | 269 | Note that the `_embed` parameter works for the front-page query, which gives you 270 | access to featured media (post-thumbnails), author information and more. 271 | 272 | #### Menus 273 | 274 | 275 | ``` 276 | $wp.menu() 277 | $wp.menu().location() 278 | ``` 279 | 280 | or 281 | 282 | ``` 283 | GET: /wp-json/wuxt/v1/menu 284 | GET: /wp-json/wuxt/v1/menu?location= 285 | ``` 286 | 287 | The **_WordPress_** Rest API is not providing an endpoint for menus by default, 288 | so we added one. We have also registered a standard menu with the location `main`, 289 | which is returned as complete menu-tree, when you request the endpoint without 290 | parameters. 291 | 292 | Don't forget to create a menu and adding it to a location in `wp-admin` when you 293 | want to use this endpoint. 294 | 295 | If you want to use multiple menus, you can request them by providing the menu 296 | location to the endpoint. 297 | 298 | #### Slugs 299 | 300 | 301 | ``` 302 | $wp.slug().name('') 303 | $wp.slug().name('').embed() 304 | ``` 305 | 306 | or 307 | 308 | ``` 309 | GET: /wp-json/wuxt/v1/slug/ 310 | GET: /wp-json/wuxt/v1/slug/?_embed 311 | ``` 312 | 313 | The **_WordPress_** Rest API is not providing an endpoint to get posts or pages 314 | by slug. That doesn't mirror the **_WordPress_** theme default behaviour, 315 | where the url-slug can point to both a page or a post. 316 | 317 | With the `slug` endpoint we add that function, which is first looking for a post 318 | with the given slug and then for a page. The `embed` parameter is working for 319 | the `slug` endpoint. 320 | 321 | #### Meta fields 322 | 323 | 324 | The **_WordPress_** Rest API does not include meta fields in the post objects by 325 | default. For two of the most common plugins, ACF and Yoast WordPress SEO, we 326 | have automatically added the values of these fields. They are located in the 327 | `meta` section of the response objects. 328 | 329 | #### Taxonomy queries 330 | 331 | 332 | Taxonomy queries are limited of the simple WordPress Rest API url structure. 333 | Especially with filtering queries, we struggled with the missing relation 334 | parameter in queries for posts by taxonomy. We added this feature with a new 335 | parameter to the WordPress API: 336 | 337 | ``` 338 | GET: /wp-json/wp/v2/posts/?categories=1,2&and=true 339 | ``` 340 | 341 | ***Note:*** *Setting the relation to "and" will cause all taxonomy queries to 342 | use it. Right now you cant query one taxonomy with "and" and another with "or".* 343 | 344 | In Nuxt you just have to use the "and" param after a post query for categories. 345 | 346 | ``` 347 | $wp.posts().categories([1,2]).param('and', true) 348 | ``` 349 | 350 | #### Geo Queries 351 | 352 | 353 | If your application has to get posts by geographical proximity, you can use the geo parameters. 354 | 355 | GET /wp-json/wp/v2/posts/?coordinates=,&distance= 356 | 357 | The coordinates parameter has to contain lat and lng, comma-separated and each value can be prefixed with the meta-key if has to be compared with (default keys: `lat`, `lng`). The distance is calculated in kilometers, postfix the value with **m** for miles. Some example queries: 358 | 359 | GET /wp-json/wp/v2/posts/?coordinates=52.585,13.373&distance=10 360 | GET /wp-json/wp/v2/posts/?coordinates=lat_mkey:52.585,lng_mkey:13.373&distance=10 361 | GET /wp-json/wp/v2/posts/?coordinates=52.585,13.373&distance=10m 362 | 363 | 364 | #### Custom post types 365 | 366 | 367 | The ***WordPress*** Rest API is providing endpoints for custom post types, as 368 | long as they are registered the right way (see the *Scaffolding* section for generating cpt-definitions). 369 | 370 | To make querying of your custom post types as easy as everything else, we added the `cpt` method to the `$wp` object. See post type queries for a 371 | fictional 'Movies' post type, below 372 | 373 | ``` 374 | $wp.cpt('movies') 375 | $wp.cpt('movies').id( 7 ) 376 | ``` 377 | 378 | The `cpt` function returns cpt-objects similar to the `posts()` or `pages()` 379 | queries, meta fields are included. 380 | 381 | ## Scripts 382 | 383 | 384 | To help you with some of the common tasks in **wuxt**, we integrated a bunch of 385 | **npm** scripts. Just install the needed packages in the root directory and you 386 | are ready to run. 387 | 388 | npm install 389 | 390 | ### Working with the containers 391 | 392 | 393 | Working with ***Docker*** is awesome, but has some drawbacks. One of them is 394 | that you have to make some changes from inside the container. To 395 | enter the **WUXT** containers, you can use the following ***npm*** 396 | scripts: 397 | 398 | npm run enter:mysql 399 | npm run enter:wp 400 | npm run enter:front 401 | 402 | You exit a container with `exit`. 403 | 404 | #### WP-CLI and yarn 405 | 406 | 407 | Two of the most 408 | common tasks are managing ***WordPress*** and installing new packages 409 | in the front-end. 410 | 411 | **WUXT** provides you with the full power of the 412 | ***WP-CLI*** tool. Check out all documentation at [https://developer.wordpress.org/cli/commands/](https://developer.wordpress.org/cli/commands/). To run any ***WP-CLI*** command inside the `wp.wuxt` 413 | container, just use the following ***npm***-script: 414 | 415 | npm run wp 416 | 417 | Examples: `npm run wp plugin list`, `npm run wp plugin install advanced-custom-fields`, `npm run wp user create wuxt me@wuxt.io` 418 | 419 | The same concept we use for ***yarn*** in the front container: 420 | 421 | npm run yarn 422 | 423 | Example: `npm run yarn add nuxt-webfontloader` 424 | 425 | The commands are checking if the containers are running and installing needed 426 | dependencies automatically. So if ***WP-CLI*** is not installed in the container it will be installed before running a `wp` command. 427 | 428 | ### Scaffolding 429 | 430 | 431 | **WUXT** allows you to generate custom post types and taxonomies via ***npm*** 432 | scripts. You can pass needed parameters as arguments. If you don't pass 433 | arguments, you will get prompted. 434 | 435 | **Scaffolding a post type** 436 | 437 | npm run scaffold:cpt 438 | 439 | # Examples: 440 | npm run scaffold:cpt 441 | npm run scaffold:cpt Movie 442 | 443 | The custom post type definition is copied into the `cpts` folder of the wuxt 444 | theme and loaded automatically by the theme. 445 | 446 | To query the new post-type you can use the `cpt` method of the **wuxt** `$wp` object. 447 | 448 | **Scaffolding a taxonomy** 449 | 450 | npm run scaffold:tax 451 | 452 | # Examples: 453 | npm run scaffold:tax 454 | npm run scaffold:tax Venue event,cafe 455 | 456 | The taxonomy definition is copied into the `taxonomies` folder of the wuxt 457 | theme and loaded automatically by the theme. 458 | 459 | ## Links 460 | 461 | 462 | [WUXT Headless WordPress API Extensions](https://wordpress.org/plugins/wuxt-headless-wp-api-extensions/): Plugin which includes all our API extensions. 463 | 464 | [Nuxt + WordPress = WUXT](https://www.danielauener.com/nuxt-js-wordpress-wuxt/): Introduction post for WUXT. 465 | 466 | ## Credits 467 | 468 | 469 | [@yashha](https://github.com/yashha/wp-nuxt/commits?author=yashha) for the excelent idea with the `$wp` object, first implemented in [https://github.com/yashha/wp-nuxt](https://github.com/yashha/wp-nuxt) 470 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------