diff --git a/.gitignore b/.gitignore index ecdf2d7..97158ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ -vendor .idea +.vscode +/vendor +/public_html/css +/public_html/js +/node_modules +/package-lock.json \ No newline at end of file diff --git a/build/router/controller/js.php b/build/router/controller/js.php new file mode 100644 index 0000000..591a94d --- /dev/null +++ b/build/router/controller/js.php @@ -0,0 +1,78 @@ + Calling URI + * + */ + public function __construct($url){ + $this->page = isset($url['page']) ? $url['page'] : ''; + $this->page = preg_match('/^@[a-z]+$/', $this->page) ? substr($this->page, 1) : null; + } + + + /* Generate _SERVER.js + * + */ + public function server(){ + header('Content-Type: text/javascript; charset=utf-8'); + + echo "window._SERVER = ".json_encode([ + + 'session' => [ + 'name' => $_SESSION['NAME'], + 'connected' => isset($_SESSION['USER']) ? count($_SESSION['USER']) > 0 : false + ] + + ])."\n"; + + } + + + /* Manage bundle hash + * + */ + public function bundle(){ + if( is_null($this->page) ){ + http_response_code(404); + die(); + } + + /* (1) Extract /public_html/js/ all .js files */ + $js_scripts = glob(__PUBLIC__.'/js/bundle/'.$this->page.'@*.js'); + + /* (2) If match pattern 'bundle@home@*.js' */ + foreach($js_scripts as $fname){ + + $bname = basename($fname); + + // if match -> load it and exit + header("Location: /js/bundle/$bname"); + die(); + + } + + /* (3) If nothing found */ + http_response_code(404); + die(); + + } + + /* POST-CALL + * + */ + public function __destruct(){ + + } + + + + } diff --git a/build/router/controller/page.php b/build/router/controller/page.php index 9a132d5..867c427 100644 --- a/build/router/controller/page.php +++ b/build/router/controller/page.php @@ -15,7 +15,6 @@ */ public function __construct($url){ $this->pagename = $url['page']; - } @@ -23,8 +22,8 @@ * */ public function load(){ - if( file_exists(__PUBLIC__."/view/".$this->pagename.".php") ) - include __PUBLIC__."/view/".$this->pagename.".php"; + if( file_exists(__PUBLIC__."/page/".$this->pagename.".php") ) + include __PUBLIC__."/page/".$this->pagename.".php"; else echo "page not found"; } diff --git a/build/router/controller/redirect.php b/build/router/controller/redirect.php index 7b10307..42cf86e 100644 --- a/build/router/controller/redirect.php +++ b/build/router/controller/redirect.php @@ -18,8 +18,8 @@ /* CALL * */ - public function homepage(){ - header('Location: /homepage/'); + public function home(){ + header('Location: /home/'); } /* POST-CALL diff --git a/config/routes.json b/config/routes.json index 8019426..d92fda3 100644 --- a/config/routes.json +++ b/config/routes.json @@ -5,6 +5,21 @@ "routes": { + + "/js/_SERVER.js": { + "methods": ["GET"], + "controller": "js:server", + "arguments": {} + }, + + "/js/bundle{page}.js": { + "methods": ["GET"], + "controller": "js:bundle", + "arguments": { + "page":"@[a-z]+" + } + }, + "/{page}/": { "methods": ["GET"], "controller": "page:load", @@ -23,7 +38,7 @@ "/{any}": { "methods": ["GET"], - "controller": "redirect:homepage", + "controller": "redirect:home", "arguments": { "any": ".*" } diff --git a/package.json b/package.json new file mode 100644 index 0000000..c8f12ad --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "ptut-vhost", + "description": "PTUT", + "version": "1.0.0", + "author": "xdrm-brackets SeekDaSky ", + "license": "MIT", + "private": true, + "scripts": { + "build:clean": "rm ./public_html/js/bundle/*.js*; exit 0", + "build:bundle": "cross-env NODE_ENV=production webpack --progress --hide-modules", + "build:dev": "cross-env NODE_ENV=development webpack --progress --hide-modules", + "build:scss": "node-sass -r --output-style compressed --output ./public_html/css ./webpack/scss", + "dev": "npm run build:clean; npm run build:dev", + "build": "npm run build:clean; npm run build:bundle; npm run build:scss" + }, + "dependencies": { + "vue": "^2.5.9" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ], + "devDependencies": { + "babel-core": "^6.26.0", + "babel-loader": "^7.1.2", + "babel-preset-env": "^1.6.0", + "babel-preset-stage-3": "^6.24.1", + "cross-env": "^5.0.5", + "css-loader": "^0.28.7", + "extract-text-webpack-plugin": "^3.0.2", + "file-loader": "^1.1.4", + "node-sass": "^4.7.2", + "sass-loader": "^6.0.6", + "vue-loader": "^13.0.5", + "vue-template-compiler": "^2.5.9", + "webpack": "^3.8.1", + "webpack-dev-server": "^2.9.5" + } +} diff --git a/public_html/page/home.php b/public_html/page/home.php new file mode 100644 index 0000000..a3235d6 --- /dev/null +++ b/public_html/page/home.php @@ -0,0 +1,35 @@ + + + + + + + + + + PTUT web title + + + + + + + + + + + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/public_html/view/homepage.php b/public_html/view/homepage.php deleted file mode 100644 index c42c4de..0000000 --- a/public_html/view/homepage.php +++ /dev/null @@ -1,7 +0,0 @@ -"; - echo "home page\n"; - print_r( DatabaseDriver::getPDO()->query("show databases;")->fetchAll() ); - echo ""; \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..5527c1b --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,86 @@ +var path = require('path') +var webpack = require('webpack') + +const ExtractTextPlugin = require("extract-text-webpack-plugin"); + +const extractSass = new ExtractTextPlugin({ + filename: "[name].css", + disable: process.env.NODE_ENV === "development" +}); + +var mod_common = { + rules: [ + { + test: /\.css$/, + use: [ + 'vue-style-loader', + 'css-loader' + ], + }, { + test: /\.vue$/, + loader: 'vue-loader', + options: { + loaders: {} // other vue-loader options go here + } + }, { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/ + }, { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { name: '[name].[ext]?[hash]' } + } + ] +}; + + + +module.exports = [ { + + name: "home", + entry: './webpack/page/home.js', + output: { + path: path.resolve(__dirname, './public_html/js/bundle'), + publicPath: '/js/bundle/', + filename: 'home@[hash].js' + }, + module: mod_common, + devtool: '#eval-source-map' + +}, { + + name: "login", + entry: './webpack/page/login.js', + output: { + path: path.resolve(__dirname, './public_html/js/bundle'), + publicPath: '/js/bundle/', + filename: 'login@[hash].js' + }, + module: mod_common, + devtool: '#eval-source-map' + +} ] + + + +if (process.env.NODE_ENV === 'production') { + module.exports.devtool = '#source-map' + // http://vue-loader.vuejs.org/en/workflow/production.html + module.exports.plugins = (module.exports.plugins || []).concat([ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"production"' + } + }), + new webpack.optimize.UglifyJsPlugin({ + sourceMap: true, + compress: { + warnings: false + } + }), + new webpack.LoaderOptionsPlugin({ + minimize: true + }) + ]) +} diff --git a/webpack/lib/api-client.js b/webpack/lib/api-client.js new file mode 100644 index 0000000..02b5769 --- /dev/null +++ b/webpack/lib/api-client.js @@ -0,0 +1,161 @@ +/* classe API */ +export class APIClient{ + + constructor(target){ + + this.target = target; + + this.xhr = []; // tableau d'objets pour les requêtes ajax + this.buffer = null; // Contiendra le buffer pour debugger si erreur de parsage + this.error = { // error constants + '-1': 'Invalid target format: "METHOD module/method"', + '-2': 'XHR error', + '-3': 'Invalid JSON response', + }; + + } + + + + /* transaction avec le serveur (http://{host}/api/) + * + * @param pTarget URI cible, format "HTTP_METHOD uri/uri/uri" + * @param pArgs Le tableu d'arguments passé en POST (attribut<->postfield) à http://{host}/api/ + * @param pHandler Fonction qui s'éxécutera lors de la réponse (1 argument -> réponse) + * @param pToken Optionnel, token d'auth pour l'api + * + *************************************************************************************************** + * + * @usecase + * 1. api.call( + * 2. "PUT newspaper/article/4" + * 2. { content: "New article content" }, + * 3. function(resp){ + * 4. alert(resp.error); + * 5. } + * 6. ); + * + */ + call(pTarget, pArgs, pHandler, pToken=null){ + + + /* (1) Check @pHandler (for dispatching errors) + ---------------------------------------------------------*/ + /* (1) Check if is a Function */ + if( !(pHandler instanceof Function) ) + throw new Error("3rd argument must be a function, but is of type '"+typeof(pHandler)+"' !"); + + + + /* (2) Check @pTarget + ---------------------------------------------------------*/ + /* (1) Check format */ + if( !/^([A-Z]+) (.+)/i.test(pTarget) ){ + pHandler({ error: -1, ErrorDescription: this.error['-1'] }); + return false; + } + + /* (2) Store locally data */ + var lHttpMethod = RegExp.$1; + var lUri = RegExp.$2; + + + + /* (3) Create form data + ---------------------------------------------------------*/ + /* (1) Create virtual form */ + var lForm = new FormData(); + + /* (2) Add attributes */ + for( var key in pArgs ){ + + // {2.1} If a file -> send as it // + if( pArgs[key] instanceof File ) + lForm.append(key, pArgs[key]); + + // {2.2} Else -> JSON stringify // + else + lForm.append(key, JSON.stringify(pArgs[key])); + + } + + + + /* (4) Create XHR request + ---------------------------------------------------------*/ + /* (1) Clean ended requests */ + for( var i = this.xhr.length-1 ; i >= 0 ; i-- ){ + + if( this.xhr[i] != null ) + break; + + this.xhr.pop(); + + } + + /* (2) Push a new entry -> fetch its index */ + i = this.xhr.push(null) - 1; + + /* (3) Create XHR object */ + this.xhr[i] = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttpRequest'); + + + + /* (5) Bind response event + ---------------------------------------------------------*/ + var self = this; // to access the buffer + + this.xhr[i].onreadystatechange = function(i){ + + /* (1) If request over */ + if( this.xhr[i].readyState == 4 ){ + + /* (2) Update buffer (for debug) */ + self.buffer = this.xhr[i].responseText; + + /* (3) If request success */ + if( [0, 200, 417].indexOf(this.xhr[i].status) > -1 ){ + + + /* (3.1) Create default response (if JSON error) */ + var response = {error:-3, ErrorDescription: self.error['-3']}; + + /* (3.2) Try to parse JSON */ + try{ response = JSON.parse(this.xhr[i].responseText); }catch(e){} + + /* (3.3) Launch @pHandler with response */ + pHandler(response); + + /* (4) If request error */ + }else + pHandler({ error:-2, ErrorDescription: self.error['-2'] }); + + /* (5) Notify current xhr instance is done */ + this.xhr[i] = null; + + } + + }.bind(this, i); + + + /* (6) Finish & send request + ---------------------------------------------------------*/ + /* (1) Open the XHR */ + this.xhr[i].open(lHttpMethod, this.target+lUri, true); + + /* (2) Manage optional token */ + if( pToken != null ) + this.xhr[i].setRequestHeader('Authorization', 'Digest '+pToken); + + /* (3) Custom header to notify we're using Ajax */ + this.xhr[i].setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + + /* (4) Make the call */ + this.xhr[i].send( lForm ); + + return true; + + } + + +} \ No newline at end of file diff --git a/webpack/page/home.js b/webpack/page/home.js new file mode 100644 index 0000000..04be724 --- /dev/null +++ b/webpack/page/home.js @@ -0,0 +1,24 @@ +/* (1) Imports +---------------------------------------------------------*/ +/* (1) NPM libs */ +import Vue from 'vue' + +/* (2) Internal libs */ +import {APIClient} from '../lib/api-client' + +/* (3) Vues */ +import wrapper_vue from '../vue/wrapper.vue' + + + +/* (2) Initialisation +---------------------------------------------------------*/ +/* (1) API */ +window.api = new APIClient(gstore.data.is_local ? 'http://ptut.com:8080/api/v/1.0/' : 'https://ptut.xdrm.io/api/v/1.0/'); + + +/* (2) Render view */ +new Vue({ + el: '#main-vue', + render: h => h(wrapper_vue) +}); \ No newline at end of file diff --git a/webpack/page/login.js b/webpack/page/login.js new file mode 100644 index 0000000..04be724 --- /dev/null +++ b/webpack/page/login.js @@ -0,0 +1,24 @@ +/* (1) Imports +---------------------------------------------------------*/ +/* (1) NPM libs */ +import Vue from 'vue' + +/* (2) Internal libs */ +import {APIClient} from '../lib/api-client' + +/* (3) Vues */ +import wrapper_vue from '../vue/wrapper.vue' + + + +/* (2) Initialisation +---------------------------------------------------------*/ +/* (1) API */ +window.api = new APIClient(gstore.data.is_local ? 'http://ptut.com:8080/api/v/1.0/' : 'https://ptut.xdrm.io/api/v/1.0/'); + + +/* (2) Render view */ +new Vue({ + el: '#main-vue', + render: h => h(wrapper_vue) +}); \ No newline at end of file diff --git a/webpack/scss/container.scss b/webpack/scss/container.scss new file mode 100644 index 0000000..e69de29 diff --git a/webpack/scss/font-loader.scss b/webpack/scss/font-loader.scss new file mode 100644 index 0000000..e69de29 diff --git a/webpack/scss/header.scss b/webpack/scss/header.scss new file mode 100644 index 0000000..e69de29 diff --git a/webpack/scss/layout.scss b/webpack/scss/layout.scss new file mode 100644 index 0000000..e69de29 diff --git a/webpack/scss/menu.scss b/webpack/scss/menu.scss new file mode 100644 index 0000000..e69de29 diff --git a/webpack/vue/wrapper.vue b/webpack/vue/wrapper.vue new file mode 100644 index 0000000..e69de29