Skip to content

Instantly share code, notes, and snippets.

@manchuck
Last active November 19, 2015 16:22
Show Gist options
  • Save manchuck/5326ffe164ceddfd8a02 to your computer and use it in GitHub Desktop.
Save manchuck/5326ffe164ceddfd8a02 to your computer and use it in GitHub Desktop.
PHPWorld SPA with Angular and Apigility
{
"name": "php_world_spa",
"main": "dist/ads-admin/index.html",
"keywords": [],
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"dist",
"node_modules",
"bower_components",
"vendor",
"test",
"tests"
],
"dependencies": {
"angular": "1.2.10",
"angular-route": "~1.2",
"angular-ui-bootstrap-bower": "~0.10",
"angular-ui-router": "0.2.8-bowratic-tedium",
"angular-resource": "~1.2",
"bootstrap": "~3.1",
"jquery": "~2.0",
"lodash": "~2.4",
"ng-tags-input": "~1.1",
"angular-loading-bar": "~0.4",
"restangular": "~1.5",
"angularjs-gravatardirective": "~1.3",
"fuelux": "~3.5"
},
"devDependencies": {
"angular-mocks": "1.2.10",
"angular-scenario": "1.2.10"
},
"resolutions": {
"angular": "1.2.10"
}
}
{
"require": {
"php": ">=5.4",
"zfcampus/zf-apigility": "~1.0",
"zfcampus/zf-apigility-documentation": "^1.0.5",
"zendframework/zendframework": "~2.5"
},
"require-dev": {
"zendframework/zftool": "dev-master",
"zendframework/zend-developer-tools": "dev-master",
"zfcampus/zf-apigility-admin": "~1.0",
"zfcampus/zf-deploy": "~1.0",
"zfcampus/zf-development-mode": "~2.0"
}
}
angular.module('ads-admin')
.config(['$stateProvider',
function ($stateProvider) {
$stateProvider.state('ads.product', {
url: '/product',
abstract: true
});
$stateProvider.state('ads.product.list', {
url: '/list',
data: {
page_title: 'Products',
breadcrumbs: ['Products']
},
views: {
'content@': {
templateUrl: 'html/product/list.html',
controller: 'ProductListController'
}
}
});
$stateProvider.state('ads.product.create', {
url: '/add',
data: {
page_title: 'Product',
breadcrumbs: ['Product', 'Create']
},
views: {
'content@': {
templateUrl: 'html/product/form.html',
controller: 'ProductFormController'
}
}
});
$stateProvider.state('ads.product.edit', {
url: '/edit/:product_id',
data: {
page_title: 'Product',
breadcrumbs: ['Product', 'Edit']
},
views: {
'content@': {
templateUrl: 'html/product/form.html',
controller: 'ProductFormController'
}
}
});
$stateProvider.state('ads.product.view', {
url: '/view/:product_id',
data: {
page_title: 'Product',
breadcrumbs: ['Product', 'View']
},
views: {
'content@': {
templateUrl: 'html/product/view.html',
controller: 'ProductViewController'
}
}
});
}])
.controller('ProductListController', ['$scope', '$rootScope', 'Restangular', '$log', 'activeAccount',
function ($scope, $rootScope, Restangular, $log) {
var product_api = Restangular.all('product');
$scope.page = 1;
$scope.per_page = 50;
$scope.loading = true;
$scope.total_products = 0;
$scope.fetchProducts = function () {
var query = {
'search' : $scope.search,
'per_page' : $scope.per_page,
'page' : $scope.page
};
$scope.loading = true;
product_api.getList(query)
.then(function (products) {
$scope.loading = false;
if (products.meta) {
var links = products.meta._links;
$scope.total_products = products.meta.total_items || 0;
}
$scope.products = products;
}, function (response) {
$scope.loading = false;
var error_message = response.data.detail || 'Unable to fetch products';
$log.error(error_message);
$.smallBox({
title : 'Error!',
content : 'Unable to load products',
color : '#C46A69',
icon : 'fa fa-warning shake animated',
timeout : 6000
});
}
);
};
}])
.controller('ProductViewController', ['$scope', 'Restangular', '$log', '$stateParams', '$rootScope', '$state', '$filter', 'dateArray',
function ($scope, Restangular, $log, $stateParams, $rootScope, $state, $filter, dateArray) {
var product_api = Restangular.all('product');
$scope.page = 1;
$scope.per_page = 50;
$scope.loading = true;
$scope.total_products = 0;
$scope.product_id = false;
$scope.errors = [];
var saveProductSuccess = function (response) {
$scope.saving = false;
$.smallBox({
title : 'Success!',
content : response.detail || 'Product saved',
color : '#739E73',
icon : 'fa fa-check animated',
timeout : 6000
});
$state.go('ads.product.list');
};
var saveProductError = function (response) {
$scope.saving = false;
var error_message = response.data.detail || 'Unable to save product';
$log.error(error_message);
$.smallBox({
title : 'Error!',
content : 'Unable to save product',
color : '#C46A69',
icon : 'fa fa-warning shake animated',
timeout : 6000
});
$scope.errors = response.data.validation_messages || {};
};
$scope.fetchProduct = function () {
if ($scope.product_id === false) {
return;
}
$scope.loading = true;
product_api.get($scope.product_id).then(
function (result) {
$scope.loading = false;
$scope.product = result;
},
function () {
$scope.loading = false;
$.smallBox({
title : 'Error!',
content : 'Unable to load product doesnt exist',
color : '#C46A69',
icon : 'fa fa-warning shake animated',
timeout : 6000
});
$state.go('ads.product.list');
}
);
};
$scope.saveProduct = function() {
$scope.saving = true;
if ($scope.product_id === false) {
product_api.post($scope.product).then(saveProductSuccess, saveProductError);
return;
}
product_api.put().then(saveProductSuccess, saveProductError);
};
}
])
.controller('ProductFormController', ['$scope', '$rootScope', 'Restangular', '$log', 'adsSettings', '$stateParams', '$state',
function ($scope, $rootScope, Restangular, $log, adsSettings, $stateParams, $state) {
var product_api = Restangular.all('product');
$scope.page = 1;
$scope.per_page = 50;
$scope.loading = true;
$scope.total_products = 0;
$scope.product_id = false;
$scope.errors = [];
var saveProductSuccess = function (response) {
$scope.saving = false;
$.smallBox({
title : 'Success!',
content : response.detail || 'Product saved',
color : '#739E73',
icon : 'fa fa-check animated',
timeout : 6000
});
$state.go('ads.product.list');
};
var saveProductError = function (response) {
$scope.saving = false;
var error_message = response.data.detail || 'Unable to save product';
$log.error(error_message);
$.smallBox({
title : 'Error!',
content : 'Unable to save product',
color : '#C46A69',
icon : 'fa fa-warning shake animated',
timeout : 6000
});
$scope.errors = response.data.validation_messages || {};
};
$scope.fetchProduct = function () {
if ($scope.product_id === false) {
return;
}
$scope.loading = true;
product_api.get($scope.product_id).then(
function (result) {
$scope.loading = false;
$scope.product = result;
},
function () {
$scope.loading = false;
$.smallBox({
title : 'Error!',
content : 'Unable to load product doesnt exist',
color : '#C46A69',
icon : 'fa fa-warning shake animated',
timeout : 6000
});
$state.go('ads.product.list');
}
);
};
$scope.saveProduct = function() {
$scope.saving = true;
if ($scope.product_id === false) {
product_api.post($scope.product).then(saveProductSuccess, saveProductError);
return;
}
product_api.put().then(saveProductSuccess, saveProductError);
};
}
]);
// Generated on 2013-12-20 using generator-angular 0.6.0
'use strict';
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,**/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function(grunt) {
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
yeoman: {
app: 'src/ads-admin',
dist: 'dist/ads-admin'
},
// Watches files for changes and runs tasks based on the changed files
watch: {
js: {
files: ['<%= yeoman.app %>/js/{,**/}*.js'],
tasks: ['newer:jshint:all', 'concat:dist']
},
jsTest: {
files: ['test/spec/{,**/}*.js'],
tasks: ['newer:jshint:test', 'karma']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
options: {
livereload: true
},
files: [
'<%= yeoman.app %>/{,**/}*.html',
'<%= yeoman.app %>/css/{,**/}*.css'
]
},
html2js: {
files: ['<%= yeoman.app %>/html/{,**/}*.html'],
tasks: ['html2js', 'concat:dist']
}
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish'),
ignores: ['<%= yeoman.app %>/js/templates.js']
},
all: [
'Gruntfile.js',
'<%= yeoman.app %>/js/{,**/}*.js'
],
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/spec/{,**/}*.js']
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>'
}
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/{,**/}*.html'],
css: ['<%= yeoman.dist %>/css/{,**/}*.css'],
options: {
assetsDirs: ['<%= yeoman.dist %>']
}
},
htmlmin: {
dist: {
options: {
// Optional configurations that you can uncomment to use
// removeCommentsFromCDATA: true,
// collapseBooleanAttributes: true,
// removeAttributeQuotes: true,
removeRedundantAttributes: true,
// useShortDoctype: true,
removeEmptyAttributes: true
// removeOptionalTags: true*/
},
files: [{
expand: true,
cwd: '<%= yeoman.app %>',
src: ['*.html', 'html/**/*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
// Allow the use of non-minsafe AngularJS files. Automatically makes it
// minsafe compatible so Uglify does not destroy the ng references
ngmin: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/js',
src: '*.js',
dest: '.tmp/concat/js'
}]
}
},
concat: {
dist: {
options: {
// Replace all 'use strict' statements in the code with a single one at the top
banner: '\'use strict\';\n',
process: function(src, filepath) {
return '// Source: ' + filepath + '\n' +
src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1');
}
},
src: ['<%= yeoman.app %>/js/{,**/}*.js'],
dest: '<%= yeoman.dist %>/js/app.js'
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'.htaccess',
'img/{,**/}*.webp',
'img/{,**/}*.gif',
'img/{,**/}*.png',
'img/favicon.ico',
'fonts/*',
'sound/*',
'js/data/**/*.json',
'ui/*'
]
}, {
expand: true,
dot: false,
cwd: '<%= yeoman.app %>/vendor/font-awesome/fonts',
dest: '<%= yeoman.dist %>/fonts/',
src: [
'*'
]
}]
},
template: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: ['js/templates.js']
}]
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
],
test: [
],
dist: [
'htmlmin'
]
},
// Test settings
karma: {
unit: {
configFile: 'karma.conf.js',
singleRun: true
}
},
//Collect all html views into single template
html2js: {
options: {
base: '<%= yeoman.app %>'
},
main: {
src: ['<%= yeoman.app %>/html/**/*.html'],
dest: '<%= yeoman.app %>/js/templates.js'
}
}
});
grunt.registerTask('monkeyPatches', function () {
// monkeypatch FileProcessor to include utf-8
var FileProcessor = require('grunt-usemin/lib/fileprocessor');
FileProcessor.prototype.replaceWithOld = FileProcessor.prototype.replaceWith;
FileProcessor.prototype.replaceWith = function replaceWith(block) {
var script = FileProcessor.prototype.replaceWithOld(block);
if (script.match(/<script src/)) {
script = script.replace('></script>', ' charset="utf-8"></script>');
}
return script;
};
});
grunt.registerTask('serve', function(target) {
grunt.task.run([
'clean:server',
'concurrent:server',
'watch'
]);
});
grunt.registerTask('test', [
'clean:server',
'concurrent:test',
'karma'
]);
grunt.registerTask('build', [
'clean:dist',
'html2js',
'useminPrepare',
'concurrent:dist',
'concat',
'ngmin',
'copy:dist',
'cssmin',
'uglify',
'monkeyPatches',
'usemin'
]);
grunt.registerTask('default', [
'newer:jshint',
'test',
'build'
]);
};
(function() {
'use strict';
angular.module('ads-admin', [
'ui.bootstrap',
'ui.router',
'restangular',
'templates-main',
])
.config(
['RestangularProvider',
function($provide, $stateProvider, $urlRouterProvider, RestangularProvider, cfpLoadingBarProvider) {
// this will Restangularize Apigility responses
// @see https://github.com/mgonto/restangular#my-response-is-actually-wrapped-with-some-metadata-how-do-i-get-the-data-in-that-case
RestangularProvider.addResponseInterceptor(
function (data, operation, what) {
var extractedData = data;
if (operation == 'getList') {
extractedData = data._embedded[what] || [];
extractedData.meta = {
_links: data._links || {},
page_count: data.page_count || 0,
page_size: data.page_size || 0,
total_items: data.total_items || 0
};
}
return extractedData;
}
);
// Restangular will send a body on remove. Apigility doesn't like that so remove the body
RestangularProvider.addRequestInterceptor(
function (element, operation) {
if (operation === 'remove') {
return null;
}
return element;
}
);
RestangularProvider.setRestangularFields({
selfLink: '_links.self.href',
cache: true
});
}]
);
})();
{
"name": "php_world_spa",
"type": "library",
"version": "1.5.0-beta",
"author": "",
"contributors": [],
"license": "MIT",
"main": "Gruntfile.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-autoprefixer": "~0.4.0",
"grunt-concurrent": "~0.4.1",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-less": "~0.9.0",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-cssmin": "~0.7.0",
"grunt-contrib-htmlmin": "~0.1.3",
"grunt-contrib-jshint": "~0.7.1",
"grunt-contrib-uglify": "~0.2.0",
"grunt-contrib-watch": "~0.5.2",
"grunt-newer": "~0.5.4",
"grunt-ngmin": "~0.0.2",
"grunt-usemin": "~2.0.0",
"jshint-stylish": "~0.1.3",
"load-grunt-tasks": "~0.2.0",
"time-grunt": "~0.2.1",
"grunt-html2js": "^0.2.4"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment