7 | #endif
8 |
--------------------------------------------------------------------------------
/client/android/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/messages_en_US.properties:
--------------------------------------------------------------------------------
1 | application.title = HTML 5 Expenses
2 | label.dragFilesHere = You can upload receipts for each expense by dragging a file on to it
3 | label.createNewReport = create a new report
4 | label.or = or
5 | label.selectExistingReport = select an existing report
--------------------------------------------------------------------------------
/server/api/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 07 12:11:05 EDT 2011
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://repo.gradle.org/gradle/distributions/gradle-1.0-milestone-3-bin.zip
7 |
--------------------------------------------------------------------------------
/server/oauth/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 05 12:59:02 CDT 2011
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://repo.gradle.org/gradle/distributions/gradle-1.0-milestone-3-bin.zip
7 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/layouts/standard/footer.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 |
4 | SpringSource
5 | Version 1.0.0 (Beta) - © Copyright 2011 SpringSource, a division of VMware
--------------------------------------------------------------------------------
/client/android/res/xml/phonegap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/config.properties:
--------------------------------------------------------------------------------
1 | debug = true
2 | dataSource.user expenses
3 | dataSource.db expenses
4 | dataSource.password expenses
5 | dataSource.host 127.0.0.1
6 | dataSource.driverClassName org.postgresql.Driver
7 | dataSource.port 5432
8 | dataSource.url jdbc:postgresql://${dataSource.host}:${dataSource.password}/${dataSource.db}
9 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/demo-data.sql:
--------------------------------------------------------------------------------
1 | insert into App (name, slug, description, organization, website, apiKey, secret, redirectUrl, grantTypes)
2 | values ('Demo', 'demo', 'Demo', 'SpringSource', 'https://www.springsource.org', '09e749d8309f4044', '189309492722aa5a', '', 'password,authorization_code,refresh_token');
3 |
4 | insert into AppDeveloper (app, developer) values (1, 1);
--------------------------------------------------------------------------------
/client/android/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-15
12 |
--------------------------------------------------------------------------------
/client/android/src/org/springsource/html5expense/Html5expense.java:
--------------------------------------------------------------------------------
1 | package org.springsource.html5expense;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.phonegap.DroidGap;
6 |
7 | public class Html5expense extends DroidGap {
8 |
9 | @Override
10 | public void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | super.loadUrl("file:///android_asset/www/index.html");
13 | }
14 | }
--------------------------------------------------------------------------------
/client/ios/html5expense/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // html5expense
4 | //
5 | // Created by Roy Clarkson on 8/30/11.
6 | // Copyright VMware, Inc. 2011. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, char *argv[]) {
12 |
13 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
14 | int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
15 | [pool release];
16 | return retVal;
17 | }
18 |
--------------------------------------------------------------------------------
/server/api/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | target:
4 | name: html5expenses
5 | url: ${name}.${target-base}
6 | framework:
7 | name: spring
8 | info:
9 | mem: 512M
10 | description: Java SpringSource Spring Application
11 | exec:
12 | mem: 512M
13 | instances: 1
14 | services:
15 | expenses-mongo:
16 | type: :mongodb
17 | expenses-postgresql:
18 | type: :postgresql
19 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/tags/develop/apps/summary.tag:
--------------------------------------------------------------------------------
1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2 | <%@ attribute name="value" required="true" rtexprvalue="true" type="com.springsource.oauthservice.develop.AppSummary" %>
3 |
4 |
5 |
">
6 |
7 |
8 |
--------------------------------------------------------------------------------
/server/api/setup.txt:
--------------------------------------------------------------------------------
1 | Here's the sequence you need to setup the database on PostgreSQL, locally.
2 |
3 | /Library/PostgreSQL/9.1/scripts/runpsql.sh; exit
4 | Server [localhost]:
5 | Database [postgres]:
6 | Port [5432]:
7 | Username [postgres]:
8 | Password for user postgres:
9 | psql (9.1.2)
10 | Type "help" for help.
11 | postgres=# create user expenses with password 'expenses' ; create database expenses ;
12 | CREATE ROLE
13 | CREATE DATABASE
14 | postgres=# grant all privileges on database expenses to expenses ;
15 | GRANT
16 | postgres=#
17 |
18 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/layouts/tiles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
2 | # For all other servers: Comment out the Log4J listener in web.xml.old to activate Log4J.
3 | log4j.rootLogger=INFO, logfile
4 |
5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
7 | log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
8 |
9 |
10 | log4j.logger.org.springframework.web=DEBUG, stdout
11 |
12 | log4j.logger.com.springsource.html5expense=DEBUG, stdout
13 |
--------------------------------------------------------------------------------
/client/ios/html5expense/Resources/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | * PhoneGap is available under *either* the terms of the modified BSD license *or* the
3 | * MIT License (2008). See https://opensource.org/licenses/alphabetical for full text.
4 | *
5 | * Copyright (c) 2011, IBM Corporation
6 | */
7 |
8 |
9 | // accessibility label for recording button
10 | "toggle audio recording" = "toggle audio recording";
11 | // notification spoken by VoiceOver when timed recording finishes
12 | "timed recording complete" = "timed recording complete";
13 | // accessibility hint for display of recorded elapsed time
14 | "recorded time in minutes and seconds" = "recorded time in minutes and seconds";
--------------------------------------------------------------------------------
/client/ios/html5expense/Resources/es.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | * PhoneGap is available under *either* the terms of the modified BSD license *or* the
3 | * MIT License (2008). See https://opensource.org/licenses/alphabetical for full text.
4 | *
5 | * Copyright (c) 2011, IBM Corporation
6 | */
7 |
8 | // accessibility label for recording button
9 | "toggle audio recording" = "grabación de audio cambiar";
10 | // notification spoken by VoiceOver when timed recording finishes
11 | "timed recording complete" = "tiempo de grabación completo";
12 | // accessibility hint for display of recorded elapsed time
13 | "recorded time in minutes and seconds" = "tiempo registrado en minutos y segundos";
--------------------------------------------------------------------------------
/server/api/todo.txt:
--------------------------------------------------------------------------------
1 | This application will have:
2 | - support for receiving eligible charges from external submission types (e.g., a batch file, or through email)
3 | - support for a web UI (connecting through a secure API)
4 | - support for a mobile UI (connecting through a secure API)
5 | - support for business logic using standard transactional services.
6 | - support for pulling information out of an itinerary on tripit and using that to help flesh out line items.
7 | - add support for deploying finished generated texpense report to box
8 | - add support for picking and adding images from box.net to expense report
9 | - add support for picking and adding images from desktop using html5 and storing *to* box.net
10 |
--------------------------------------------------------------------------------
/client/shared/www/jquery.currency.1.0.0.min.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2009 Michael Manning (actingthemaggot.com) Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.*/
2 | (function(A){A.fn.extend({currency:function(B){var C={s:",",d:".",c:2};C=A.extend({},C,B);return this.each(function(){var D=(C.n||A(this).text());D=(typeof D==="number")?D:((/\./.test(D))?parseFloat(D):parseInt(D)),s=D<0?"-":"",i=parseInt(D=Math.abs(+D||0).toFixed(C.c))+"",j=(j=i.length)>3?j%3:0;A(this).text(s+(j?i.substr(0,j)+C.s:"")+i.substr(j).replace(/(\d{3})(?=\d)/g,"$1"+C.s)+(C.c?C.d+Math.abs(D-i).toFixed(C.c).slice(2):""));return this})}})})(jQuery);jQuery.currency=function(){var A=jQuery("").text(arguments[0]).currency(arguments[1]);return A.text()};
--------------------------------------------------------------------------------
/server/api/src/main/resources/oauth.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/views/develop/apps/view.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 | <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
4 | <%@ taglib tagdir="/WEB-INF/tags/develop/apps" prefix="apps" %>
5 |
6 | App Settings
7 |
8 |
9 |
10 |
11 | API key
12 | ${app.apiKey}
13 | Secret
14 | ${app.secret}
15 |
16 |
17 | " method="post">
18 |
19 | Delete
20 |
21 |
22 | ">Edit details
23 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/layouts/standard/page.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 | <%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
23 |
24 |
--------------------------------------------------------------------------------
/client/ios/html5expense/Classes/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // html5expense
4 | //
5 | // Created by Roy Clarkson on 8/30/11.
6 | // Copyright VMware, Inc. 2011. All rights reserved.
7 | //
8 |
9 | #import
10 | #ifdef PHONEGAP_FRAMEWORK
11 | #import
12 | #else
13 | #import "PhoneGapDelegate.h"
14 | #endif
15 |
16 | @interface AppDelegate : PhoneGapDelegate {
17 |
18 | NSString* invokeString;
19 | }
20 |
21 | // invoke string is passed to your app on launch, this is only valid if you
22 | // edit html5expense.plist to add a protocol
23 | // a simple tutorial can be found here :
24 | // https://iphonedevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html
25 |
26 | @property (copy) NSString* invokeString;
27 |
28 | @end
29 |
30 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/setup/schema.sql:
--------------------------------------------------------------------------------
1 | -- drop table if exists ELIGIBLE_CHARGE;
2 | -- drop table if exists EXPENSE;
3 | -- drop table if exists EXPENSE_REPORT;
4 | -- create table ELIGIBLE_CHARGE (id serial, amount decimal(19,2), category varchar(255), charge_date timestamp, merchant varchar(255), primary key (id));
5 | -- create table EXPENSE (id serial, amount decimal(19,2), category varchar(255), chargeId bigint, expense_date timestamp, flag varchar(255), merchant varchar(255), receipt varchar(255), receiptExtension varchar(255), expenseReport_id bigint, primary key (id));
6 | -- create table EXPENSE_REPORT (id serial, purpose varchar(255), receiptRequiredAmount decimal(19,2), state varchar(255), primary key (id));
7 | -- alter table EXPENSE add constraint FKDCC054382876AD53 foreign key (expenseReport_id) references EXPENSE_REPORT (id) match full;
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/views/develop/apps/appFormFragment.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 | <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
4 |
5 | Name
6 |
7 | Description
8 |
9 | Organization
10 |
11 | Website
12 |
13 | Callback URL
14 |
15 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/views/develop/apps/list.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 | <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
4 | <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
5 | <%@ taglib tagdir="/WEB-INF/tags/develop/apps" prefix="apps" %>
6 |
7 | Apps
8 |
9 |
10 | You have not registered any apps.
11 |
12 |
13 | You have registered the following apps:
14 |
15 |
16 |
17 |
20 |
21 |
22 | ">Register App
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/views/develop/apps/edit.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 | <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
4 | <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
20 | Save
21 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/views/develop/apps/new.jsp:
--------------------------------------------------------------------------------
1 | <%@ page session="false" %>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 | <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
4 | <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
5 |
6 |
7 |
8 |
17 |
18 |
19 |
20 | Register
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTML5 EXPENSE is no longer actively maintained by VMware, Inc. #
2 |
3 | ## Overview ##
4 |
5 | The HTML5 Expense project is an expense reporting reference app demonstrating HTML5 and cross-platform mobile development. The project includes the following components.
6 |
7 | ### Client ###
8 |
9 | The two native clients share the same web source built using PhoneGap, jQuery, and jQuery Mobile.
10 |
11 | * ANDROID - native Android PhoneGap project files
12 | * iOS - native iOS PhoneGap project files
13 | * SHARED - shared web source and JavaScript
14 |
15 | ### Server ###
16 |
17 | * API - Spring MVC application to which the client makes RESTful requests.
18 | * OAUTH - Spring application built with Spring Security Oauth that manages access to the API.
19 |
20 | ## More Information ##
21 |
22 | View the README files within the client and server directories for more information on each of those components.
23 |
--------------------------------------------------------------------------------
/server/oauth/README.md:
--------------------------------------------------------------------------------
1 | # HTML5 EXPENSE - OAUTH #
2 |
3 | ## Overview ##
4 |
5 | The OAuth app is used to authenticate the client. It is deployed separately from the API app.
6 |
7 | ## Project Structure ##
8 |
9 | the web application
10 |
11 | * src - all source code
12 | * main/java - backend java source code
13 | * main/webapp/assets - static sources including css, javascript, and image files
14 | * main/webapp/WEB-INF/views - template sources that generic dynamic views
15 | * test/ - test code
16 |
17 | ## Build the App ##
18 |
19 | To build a deployable web app archive (.war):
20 |
21 | ./gradlew build
22 |
23 | See build/libs/oauth.war
24 |
25 | ## Deploy to Cloud Foundry ##
26 |
27 | vmc push --path build/libs
28 |
29 | The OAuth app requires a PostgreSQL service to be installed in Cloud Foundry. When pushing this to a new application in Cloud Foundry, add a PostgreSQL service and name it "tokendb".
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/views/develop/apps/tiles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/setup/demo-data.sql:
--------------------------------------------------------------------------------
1 | insert into ELIGIBLE_CHARGE (id,amount, category, charge_date, merchant) values (nextval('hibernate_sequence'), 242.24, 'travel', now() , 'southwest');
2 | insert into ELIGIBLE_CHARGE (id,amount, category, charge_date, merchant) values (nextval('hibernate_sequence'), 23.5, 'food', now() , 'starbucks');
3 | insert into ELIGIBLE_CHARGE (id,amount, category, charge_date, merchant) values (nextval('hibernate_sequence'), 250.0, 'travel', now() , 'united airlines');
4 | insert into ELIGIBLE_CHARGE (id,amount, category, charge_date, merchant) values (nextval('hibernate_sequence'), 4500.0, 'computer supplies', now() , 'apple computer');
5 | insert into ELIGIBLE_CHARGE (id,amount, category, charge_date, merchant) values (nextval('hibernate_sequence'), 150.0, 'travel', now() , 'american airlines');
6 | insert into ELIGIBLE_CHARGE (id,amount, category, charge_date, merchant) values (nextval('hibernate_sequence'), 15.5, 'personal', now() , 'ghostbusters');
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/oauth.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | ========================================================================
2 | == NOTICE file corresponding to section 4 d of the Apache License, ==
3 | == Version 2.0, in this case for the Spring Android distribution. ==
4 | ========================================================================
5 |
6 | This product includes software developed by
7 | the Apache Software Foundation (https://www.apache.org).
8 |
9 | The end-user documentation included with a redistribution, if any,
10 | must include the following acknowledgement:
11 |
12 | "This product includes software developed by SpringSource
13 | (https://www.springsource.com)."
14 |
15 | Alternatively, this acknowledgement may appear in the software itself,
16 | if and wherever such third-party acknowledgements normally appear.
17 |
18 | The names "Spring", "SpringSource", and "html5expense" must
19 | not be used to endorse or promote products derived from this software
20 | without prior written permission. For written permission, please contact
21 | enquiries@springsource.com.
22 |
--------------------------------------------------------------------------------
/client/android/res/xml/plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/DataSourceConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import com.mongodb.Mongo;
19 | import org.springframework.data.mongodb.MongoDbFactory;
20 | import org.springframework.data.mongodb.core.MongoTemplate;
21 |
22 | import javax.sql.DataSource;
23 |
24 | /**
25 | *
26 | */
27 | public interface DataSourceConfig {
28 | DataSource dataSource() throws Exception ;
29 |
30 | MongoTemplate mongoTemplate () throws Exception;
31 | }
32 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/ComponentConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.config;
17 |
18 | import org.springframework.context.annotation.ComponentScan;
19 | import org.springframework.context.annotation.ComponentScan.Filter;
20 | import org.springframework.context.annotation.Configuration;
21 |
22 | @Configuration
23 | @ComponentScan(basePackages="com.springsource.oauthservice", excludeFilters={ @Filter(Configuration.class)} )
24 | public class ComponentConfig {
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/schema.sql:
--------------------------------------------------------------------------------
1 | create table App (id serial,
2 | name varchar not null unique,
3 | slug varchar not null unique,
4 | description varchar not null,
5 | organization varchar,
6 | website varchar,
7 | apiKey varchar not null unique,
8 | secret varchar not null unique,
9 | redirectUrl varchar,
10 | resourceIds varchar,
11 | scope varchar,
12 | grantTypes varchar,
13 | authorities varchar,
14 | primary key (id));
15 |
16 | create table AppDeveloper (app bigint,
17 | developer varchar,
18 | primary key (app, developer),
19 | foreign key (app) references App(id) on delete cascade);
20 |
21 | -- Changing the names of the oauth_access_token and oauth_refresh_token tables would involve overriding 9 different queries in JdbcOAuth2ProviderTokenServices.
22 | -- Will leave them as-is for now.
23 | create table oauth_access_token (
24 | token_id VARCHAR(256),
25 | token BYTEA,
26 | authentication BYTEA,
27 | refresh_token VARCHAR(256)
28 | );
29 |
30 | create table oauth_refresh_token (
31 | token_id VARCHAR(256),
32 | token BYTEA,
33 | authentication BYTEA
34 | );
35 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/services/Flag.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.services;
17 |
18 | public class Flag {
19 |
20 | private final Integer expenseId;
21 |
22 | private final String value;
23 |
24 | public Flag(Integer expenseId, String value) {
25 | this.expenseId = expenseId;
26 | this.value = value;
27 | }
28 |
29 | public Integer getExpenseId() {
30 | return expenseId;
31 | }
32 |
33 | public String getValue() {
34 | return value;
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/develop/AppRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.develop;
17 |
18 | import java.util.List;
19 |
20 | public interface AppRepository {
21 |
22 | List findAppSummaries(String developerId);
23 |
24 | App findAppBySlug(String developerId, String slug);
25 |
26 | String createApp(String developerId, AppForm form);
27 |
28 | AppForm getNewAppForm();
29 |
30 | AppForm getAppForm(String developerId, String slug);
31 |
32 | String updateApp(String developerId, String slug, AppForm form);
33 |
34 | void deleteApp(String developerId, String slug);
35 | }
36 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/State.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense;
17 |
18 | /**
19 | * Various expense report states.
20 | *
21 | * @author Keith Donald
22 | */
23 | public enum State {
24 |
25 | /**
26 | * The expense report is new and has not yet been submitted.
27 | */
28 | NEW,
29 |
30 | /**
31 | * The expense report has been submitted and is locked under review.
32 | */
33 | IN_REVIEW,
34 |
35 | /**
36 | * The expense report was rejected and can be modified.
37 | */
38 | REJECTED,
39 |
40 | /**
41 | * The expense report has been approved and is now closed.
42 | */
43 | APPROVED;
44 | }
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/DataConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.config;
17 |
18 | import javax.inject.Inject;
19 | import javax.sql.DataSource;
20 |
21 | import org.springframework.context.annotation.Bean;
22 | import org.springframework.context.annotation.Configuration;
23 | import org.springframework.context.annotation.ImportResource;
24 | import org.springframework.jdbc.core.JdbcTemplate;
25 |
26 | @Configuration
27 | @ImportResource("classpath:com/springsource/oauthservice/config/data.xml")
28 | public class DataConfig {
29 |
30 | @Inject
31 | private DataSource dataSource;
32 |
33 | @Bean
34 | public JdbcTemplate jdbcTemplate() {
35 | return new JdbcTemplate(dataSource);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/client/android/proguard.cfg:
--------------------------------------------------------------------------------
1 | -optimizationpasses 5
2 | -dontusemixedcaseclassnames
3 | -dontskipnonpubliclibraryclasses
4 | -dontpreverify
5 | -verbose
6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7 |
8 | -keep public class * extends android.app.Activity
9 | -keep public class * extends android.app.Application
10 | -keep public class * extends android.app.Service
11 | -keep public class * extends android.content.BroadcastReceiver
12 | -keep public class * extends android.content.ContentProvider
13 | -keep public class * extends android.app.backup.BackupAgentHelper
14 | -keep public class * extends android.preference.Preference
15 | -keep public class com.android.vending.licensing.ILicensingService
16 |
17 | -keepclasseswithmembernames class * {
18 | native ;
19 | }
20 |
21 | -keepclasseswithmembers class * {
22 | public (android.content.Context, android.util.AttributeSet);
23 | }
24 |
25 | -keepclasseswithmembers class * {
26 | public (android.content.Context, android.util.AttributeSet, int);
27 | }
28 |
29 | -keepclassmembers class * extends android.app.Activity {
30 | public void *(android.view.View);
31 | }
32 |
33 | -keepclassmembers enum * {
34 | public static **[] values();
35 | public static ** valueOf(java.lang.String);
36 | }
37 |
38 | -keep class * implements android.os.Parcelable {
39 | public static final android.os.Parcelable$Creator *;
40 | }
41 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/ec-loader.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/controllers/ExpenseReportingViewController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.controllers;
17 |
18 | import com.springsource.html5expense.ExpenseReportingService;
19 | import org.springframework.stereotype.Controller;
20 | import org.springframework.ui.ModelMap;
21 | import org.springframework.web.bind.annotation.RequestMapping;
22 | import org.springframework.web.bind.annotation.RequestMethod;
23 |
24 | import javax.inject.Inject;
25 |
26 | @Controller
27 | public class ExpenseReportingViewController {
28 |
29 | @Inject
30 | private ExpenseReportingService expenseReportingService;
31 |
32 | @RequestMapping(value = "/home", method = RequestMethod.GET)
33 | public String showExpenses(ModelMap map) throws Exception {
34 | return "receipts";
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/server/api/src/main/resources/security.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/data.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | classpath:com/springsource/oauthservice/config/schema.sql
20 | classpath:com/springsource/oauthservice/config/demo-data.sql
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/integrations/EligibleChargeProcessorHeaders.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.integrations;
17 |
18 | /**
19 | * well known headers that need to be present when constructing
20 | * {@link com.springsource.html5expense.EligibleCharge}
21 | * contributions in routing code.
22 | *
23 | * @author Josh Long
24 | */
25 | public class EligibleChargeProcessorHeaders {
26 |
27 | /**
28 | * Well known header for the date of the Eligible charge
29 | */
30 | static public final String EC_DATE = "ec_date";
31 | /**
32 | * well known header for the merchant
33 | */
34 | static public final String EC_MERCHANT = "ec_merchant";
35 |
36 | /**
37 | * well known header for the amount
38 | */
39 | static public final String EC_AMOUNT = "ec_amount";
40 |
41 | /**
42 | * well known header for the category
43 | */
44 | static public final String EC_CATEGORY = "ec_category";
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/client/ios/html5expense/html5expense-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIconFiles
6 |
7 | icon.png
8 | icon@2x.png
9 | icon-72.png
10 |
11 | UISupportedInterfaceOrientations~ipad
12 |
13 | UIInterfaceOrientationPortrait
14 | UIInterfaceOrientationLandscapeLeft
15 | UIInterfaceOrientationPortraitUpsideDown
16 | UIInterfaceOrientationLandscapeRight
17 |
18 | UISupportedInterfaceOrientations
19 |
20 | UIInterfaceOrientationPortrait
21 |
22 | CFBundleDevelopmentRegion
23 | English
24 | CFBundleDisplayName
25 | ${PRODUCT_NAME}
26 | CFBundleExecutable
27 | ${EXECUTABLE_NAME}
28 | CFBundleIconFile
29 | icon.png
30 | CFBundleIdentifier
31 | org.springsource.html5expense
32 | CFBundleInfoDictionaryVersion
33 | 6.0
34 | CFBundleName
35 | ${PRODUCT_NAME}
36 | CFBundlePackageType
37 | APPL
38 | CFBundleSignature
39 | ????
40 | CFBundleVersion
41 | 1.0
42 | LSRequiresIPhoneOS
43 |
44 | NSMainNibFile
45 |
46 | NSMainNibFile~ipad
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/utils/SlugUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.utils;
17 |
18 | import java.text.Normalizer;
19 | import java.text.Normalizer.Form;
20 | import java.util.Locale;
21 | import java.util.regex.Pattern;
22 |
23 | /**
24 | * Utilities for generating slugs: a short, meaningful name for a resource that can be used in the resource's friendly URL path.
25 | * @author Keith Donald
26 | */
27 | public class SlugUtils {
28 |
29 | private static final Pattern NONLATIN = Pattern.compile("[^\\w-]");
30 |
31 | private static final Pattern WHITESPACE = Pattern.compile("[\\s]");
32 |
33 | /**
34 | * Convert the String input to a slug.
35 | */
36 | public static String toSlug(String input) {
37 | if (input == null) {
38 | throw new IllegalArgumentException("Input cannot be null");
39 | }
40 | String nowhitespace = WHITESPACE.matcher(input).replaceAll("-");
41 | String normalized = Normalizer.normalize(nowhitespace, Form.NFD);
42 | String slug = NONLATIN.matcher(normalized).replaceAll("");
43 | return slug.toLowerCase(Locale.ENGLISH);
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/develop/AppSummary.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.develop;
17 |
18 | public class AppSummary {
19 |
20 | private final String name;
21 |
22 | private final String iconUrl;
23 |
24 | private final String description;
25 |
26 | private final String slug;
27 |
28 | public AppSummary(String name, String iconUrl, String description, String slug) {
29 | this.name = name;
30 | this.iconUrl = iconUrl;
31 | this.description = description;
32 | this.slug = slug;
33 | }
34 |
35 | /**
36 | * The name of the app.
37 | */
38 | public String getName() {
39 | return name;
40 | }
41 |
42 | /**
43 | * A link to the application's icon, suitable for display as a thumbnail.
44 | */
45 | public String getIconUrl() {
46 | return iconUrl;
47 | }
48 |
49 | /**
50 | * Short description of the app.
51 | */
52 | public String getDescription() {
53 | return description;
54 | }
55 |
56 | /**
57 | * Short, unique, and meaningful key for the application.
58 | * Used in browser URLs that reference the app resource.
59 | */
60 | public String getSlug() {
61 | return slug;
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/client/ios/html5expense/PhoneGap.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TopActivityIndicator
6 | gray
7 | EnableLocation
8 |
9 | EnableViewportScale
10 |
11 | AutoHideSplashScreen
12 |
13 | ShowSplashScreenSpinner
14 |
15 | MediaPlaybackRequiresUserAction
16 |
17 | AllowInlineMediaPlayback
18 |
19 | OpenAllWhitelistURLsInWebView
20 |
21 | ExternalHosts
22 |
23 | 127.0.0.1
24 | localhost
25 | *.phonegap.com
26 | *.cloudfoundry.com
27 |
28 | Plugins
29 |
30 | com.phonegap.accelerometer
31 | PGAccelerometer
32 | com.phonegap.camera
33 | PGCamera
34 | com.phonegap.connection
35 | PGConnection
36 | com.phonegap.contacts
37 | PGContacts
38 | com.phonegap.debugconsole
39 | PGDebugConsole
40 | com.phonegap.file
41 | PGFile
42 | com.phonegap.filetransfer
43 | PGFileTransfer
44 | com.phonegap.geolocation
45 | PGLocation
46 | com.phonegap.notification
47 | PGNotification
48 | com.phonegap.media
49 | PGSound
50 | com.phonegap.mediacapture
51 | PGCapture
52 | com.phonegap.splashscreen
53 | PGSplashScreen
54 |
55 |
56 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/web/CloudAwareApplicationContextInitializer.java:
--------------------------------------------------------------------------------
1 | package com.springsource.html5expense.web;
2 |
3 | import org.cloudfoundry.runtime.env.CloudEnvironment;
4 | import org.springframework.context.ApplicationContextInitializer;
5 | import org.springframework.core.env.ConfigurableEnvironment;
6 | import org.springframework.web.context.ConfigurableWebApplicationContext;
7 |
8 | /**
9 | *
10 | * {@link ApplicationContextInitializer application context initializers} are Spring callback interfaces that let you
11 | * tailor the Spring application context before it's run. This is useful if you want to register custom classes to be run,
12 | * or conditionally set the active profile or anything else.
13 | *
14 | * Here, we're using this callback, which runs right before the {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet} machinery
15 | * instantiates the Spring {@link org.springframework.context.ApplicationContext application context}, to detect whether
16 | * the application's running inside of Cloud Foundry (by asking the {@link org.cloudfoundry.runtime.env.CloudEnvironment#isCloudFoundry() cloud foundry API}) and
17 | * set the active profile accordingly.
18 | *
19 | *
20 | * @author Josh Long
21 | */
22 | public class CloudAwareApplicationContextInitializer implements ApplicationContextInitializer {
23 | @Override
24 | public void initialize(ConfigurableWebApplicationContext applicationContext) {
25 | CloudEnvironment cloudEnvironment = new CloudEnvironment();
26 | ConfigurableEnvironment environment = applicationContext.getEnvironment();
27 | String profile = cloudEnvironment.isCloudFoundry() ? "cloud": "local" ;
28 | environment.setActiveProfiles(profile);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/client/shared/www/device-detection.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | var iphone = "iphone";
18 | var ipod = "ipod";
19 | var android = "android";
20 |
21 | var uagent = navigator.userAgent.toLowerCase();
22 |
23 | function DetectDevice() {
24 | console.log(uagent);
25 | if ((uagent.search(iphone) > -1) || (uagent.search(ipod) > -1)) {
26 | console.log('iOS Device');
27 | document.write('\x3Cscript type="text/javascript" src="phonegap-ios-1.3.0.js">\x3C/script>');
28 | } else if (uagent.search(android) > -1) {
29 | console.log('Android Device');
30 | document.write('\x3Cscript type="text/javascript" src="phonegap-android-1.3.0.js">\x3C/script>');
31 | } else {
32 | console.log('Browser');
33 | function deviceready() {
34 | var e = document.createEvent('Events');
35 | e.initEvent('deviceready');
36 | document.dispatchEvent(e);
37 | }
38 | if (typeof PhoneGap != 'undefined') {
39 | navigator.camera = {};
40 | navigator.camera.getPicture = function() {
41 | alert('sorry, camera not supported in the browser: yet!');
42 | }
43 | window.PhoneGap = {};
44 | deviceready();
45 | }
46 | }
47 | }
48 |
49 | DetectDevice();
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import com.springsource.html5expense.security.EndpointTokenServices;
19 | import org.springframework.context.annotation.Bean;
20 | import org.springframework.context.annotation.Configuration;
21 | import org.springframework.context.annotation.ImportResource;
22 | import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
23 | import org.springframework.security.web.AuthenticationEntryPoint;
24 | import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
25 |
26 | @Configuration
27 | @ImportResource("classpath:security.xml")
28 | public class SecurityConfig {
29 |
30 | @Bean
31 | public AuthenticationEntryPoint entryPoint() {
32 | return new Http403ForbiddenEntryPoint();
33 | }
34 |
35 | // OAuth beans
36 | @Bean
37 | public ResourceServerTokenServices tokenServices() {
38 | // TODO: Pull the authentication endpoint URL from the environment
39 | // Or, if the oauth service becomes a "native" CF service, then this whole bean could be consumed as a CF service.
40 | return new EndpointTokenServices("https://haboauth.cloudfoundry.com/me/authentication");
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/LocalDataSourceConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import com.mongodb.Mongo;
19 | import org.postgresql.Driver;
20 | import org.springframework.context.annotation.Bean;
21 | import org.springframework.context.annotation.Configuration;
22 | import org.springframework.context.annotation.Profile;
23 | import org.springframework.data.mongodb.core.MongoTemplate;
24 | import org.springframework.jdbc.datasource.SimpleDriverDataSource;
25 |
26 | import javax.sql.DataSource;
27 |
28 | @Configuration
29 | @Profile("local")
30 | public class LocalDataSourceConfig implements DataSourceConfig {
31 |
32 | @Bean
33 | @Override
34 | public DataSource dataSource() throws Exception {
35 | SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
36 | dataSource.setUrl(String.format("jdbc:postgresql://%s:%s/%s", "127.0.0.1", 5432, "expenses"));
37 | dataSource.setDriverClass(Driver.class);
38 | dataSource.setUsername("expenses");
39 | dataSource.setPassword("expenses");
40 | return dataSource;
41 | }
42 |
43 | @Bean
44 | @Override
45 | public MongoTemplate mongoTemplate() throws Exception {
46 | return new MongoTemplate(new Mongo("127.0.0.1"), "expensesfs");
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/integrations/EligibleChargeProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.integrations;
17 |
18 | import com.springsource.html5expense.ExpenseReportingService;
19 | import org.springframework.integration.annotation.Header;
20 | import org.springframework.integration.annotation.ServiceActivator;
21 |
22 | import javax.inject.Inject;
23 | import java.math.BigDecimal;
24 | import java.util.Date;
25 |
26 | /**
27 | * This is the gateway for all incoming eligible charges. There are lots of ways
28 | * to submit eligible charges, but we'll assume that they all converge on this
29 | * endpoint thanks to Spring Integration's normalization and routing prowess.
30 | *
31 | * @author Josh Long
32 | */
33 | public class EligibleChargeProcessor {
34 |
35 | @Inject
36 | private ExpenseReportingService expenseReportingService;
37 |
38 | @ServiceActivator
39 | public void processNewEligibleCharge(@Header(EligibleChargeProcessorHeaders.EC_DATE) Date date,
40 | @Header(EligibleChargeProcessorHeaders.EC_MERCHANT) String merchant,
41 | @Header(EligibleChargeProcessorHeaders.EC_CATEGORY) String category,
42 | @Header(EligibleChargeProcessorHeaders.EC_AMOUNT) BigDecimal amount) throws Exception {
43 | this.expenseReportingService.createEligibleCharge(date, merchant, category, amount);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/WebConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.config;
17 |
18 | import org.springframework.context.annotation.Bean;
19 | import org.springframework.context.annotation.Configuration;
20 | import org.springframework.web.servlet.ViewResolver;
21 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
22 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
23 | import org.springframework.web.servlet.view.UrlBasedViewResolver;
24 | import org.springframework.web.servlet.view.tiles2.TilesConfigurer;
25 | import org.springframework.web.servlet.view.tiles2.TilesView;
26 |
27 | import com.springsource.oauthservice.Bootstrap;
28 |
29 | @Configuration
30 | @EnableWebMvc
31 | public class WebConfig extends WebMvcConfigurerAdapter {
32 |
33 | @Bean(initMethod="go")
34 | public Bootstrap bootstrap() {
35 | return new Bootstrap();
36 | }
37 |
38 | @Bean
39 | public ViewResolver viewResolver() {
40 | UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
41 | viewResolver.setViewClass(TilesView.class);
42 | return viewResolver;
43 | }
44 |
45 | @Bean
46 | public TilesConfigurer tilesConfigurer() {
47 | TilesConfigurer configurer = new TilesConfigurer();
48 | configurer.setDefinitions(new String[] {
49 | "/WEB-INF/layouts/tiles.xml",
50 | "/WEB-INF/views/**/tiles.xml"
51 | });
52 | configurer.setCheckRefresh(true);
53 | return configurer;
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/EligibleCharge.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense;
17 |
18 | import javax.persistence.Entity;
19 | import javax.persistence.GeneratedValue;
20 | import javax.persistence.Id;
21 | import javax.persistence.Table;
22 | import java.math.BigDecimal;
23 | import java.util.Date;
24 |
25 | /**
26 | * Simple entity to describe a charge that has yet to be reconciled.
27 | */
28 | @Entity
29 | @Table(name = "ELIGIBLE_CHARGE")
30 | public class EligibleCharge {
31 |
32 | @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
33 | @Id
34 | private Long id;
35 |
36 | public Long getId() {
37 | return this.id;
38 | }
39 |
40 | private String merchant, category;
41 | private Date charge_date;
42 | private BigDecimal amount;
43 |
44 | public EligibleCharge() {
45 | }
46 |
47 | public EligibleCharge(Date charge_date, String merchant, String category, BigDecimal bigDecimal) {
48 | this.merchant = merchant;
49 | this.category = category;
50 | this.charge_date = charge_date;
51 | this.amount = bigDecimal;
52 | }
53 |
54 | public Date getDate() {
55 | return charge_date;
56 | }
57 |
58 | public String getMerchant() {
59 | return merchant;
60 | }
61 |
62 | public String getCategory() {
63 | return category;
64 | }
65 |
66 | public BigDecimal getAmount() {
67 | return amount;
68 | }
69 |
70 | public Long getI() {
71 | return this.id;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/client/shared/www/jquery.json-2.3.min.js:
--------------------------------------------------------------------------------
1 |
2 | (function($){var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};$.toJSON=typeof JSON==='object'&&JSON.stringify?JSON.stringify:function(o){if(o===null){return'null';}
3 | var type=typeof o;if(type==='undefined'){return undefined;}
4 | if(type==='number'||type==='boolean'){return''+o;}
5 | if(type==='string'){return $.quoteString(o);}
6 | if(type==='object'){if(typeof o.toJSON==='function'){return $.toJSON(o.toJSON());}
7 | if(o.constructor===Date){var month=o.getUTCMonth()+1,day=o.getUTCDate(),year=o.getUTCFullYear(),hours=o.getUTCHours(),minutes=o.getUTCMinutes(),seconds=o.getUTCSeconds(),milli=o.getUTCMilliseconds();if(month<10){month='0'+month;}
8 | if(day<10){day='0'+day;}
9 | if(hours<10){hours='0'+hours;}
10 | if(minutes<10){minutes='0'+minutes;}
11 | if(seconds<10){seconds='0'+seconds;}
12 | if(milli<100){milli='0'+milli;}
13 | if(milli<10){milli='0'+milli;}
14 | return'"'+year+'-'+month+'-'+day+'T'+
15 | hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
16 | if(o.constructor===Array){var ret=[];for(var i=0;i
2 |
5 |
6 |
7 |
8 | contextClass
9 | org.springframework.web.context.support.AnnotationConfigWebApplicationContext
10 |
11 |
12 | contextInitializerClasses
13 | com.springsource.html5expense.web.CloudAwareApplicationContextInitializer
14 |
15 |
16 |
17 | contextConfigLocation
18 | com.springsource.html5expense.config.WebConfig
19 |
20 |
21 |
22 | hiddenHttpMethodFilter
23 | org.springframework.web.filter.HiddenHttpMethodFilter
24 |
25 |
26 | hiddenHttpMethodFilter
27 | /
28 | appServlet
29 |
30 |
31 |
32 |
33 |
34 | org.springframework.web.context.ContextLoaderListener
35 |
36 |
37 |
38 |
39 | appServlet
40 | org.springframework.web.servlet.DispatcherServlet
41 |
42 | contextConfigLocation
43 |
44 |
45 | 1
46 |
47 |
48 |
49 | appServlet
50 | /
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/security.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/develop/AppForm.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.develop;
17 |
18 | import org.hibernate.validator.constraints.NotEmpty;
19 |
20 | /**
21 | * Model backing the "Register New App" and "Update Existing App" forms.
22 | * @author Craig Walls
23 | */
24 | public class AppForm {
25 |
26 | @NotEmpty
27 | private String name;
28 |
29 | @NotEmpty
30 | private String description;
31 |
32 | private String organization;
33 |
34 | private String website;
35 |
36 | private String callbackUrl;
37 |
38 | /**
39 | * The name of the app.
40 | */
41 | public String getName() {
42 | return name;
43 | }
44 |
45 | public void setName(String name) {
46 | this.name = name;
47 | }
48 |
49 | /**
50 | * A short description of the app.
51 | */
52 | public String getDescription() {
53 | return description;
54 | }
55 |
56 | public void setDescription(String description) {
57 | this.description = description;
58 | }
59 |
60 | /**
61 | * The organization that publishes the app.
62 | */
63 | public String getOrganization() {
64 | return organization;
65 | }
66 |
67 | public void setOrganization(String organization) {
68 | this.organization = organization;
69 | }
70 |
71 | /**
72 | * The website you can visit to get more information on the app.
73 | */
74 | public String getWebsite() {
75 | return website;
76 | }
77 |
78 | public void setWebsite(String website) {
79 | this.website = website;
80 | }
81 |
82 | /**
83 | * The URL members should be redirected back to after they connect to the app.
84 | */
85 | public String getCallbackUrl() {
86 | return callbackUrl;
87 | }
88 |
89 | public void setCallbackUrl(String callbackUrl) {
90 | this.callbackUrl = callbackUrl;
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/client/android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/server/api/src/test/java/com/springsource/html5expense/services/TestJpaExpenseReportingService.java:
--------------------------------------------------------------------------------
1 | package com.springsource.html5expense.services;
2 |
3 | import com.springsource.html5expense.EligibleCharge;
4 | import com.springsource.html5expense.Expense;
5 | import com.springsource.html5expense.ExpenseReportingService;
6 | import com.springsource.html5expense.config.ComponentConfig;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.springframework.test.context.ActiveProfiles;
10 | import org.springframework.test.context.ContextConfiguration;
11 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 | import org.springframework.test.context.support.AnnotationConfigContextLoader;
13 | import org.springframework.test.context.transaction.TransactionConfiguration;
14 | import org.springframework.transaction.annotation.Transactional;
15 |
16 | import javax.inject.Inject;
17 | import java.util.ArrayList;
18 | import java.util.Collection;
19 | import java.util.List;
20 |
21 | import static org.junit.Assert.*;
22 |
23 | /**
24 | * Tests the {@link JpaExpenseReportingService JPA expense reporting service}.
25 | *
26 | * @author Josh Long
27 | */
28 | @RunWith(SpringJUnit4ClassRunner.class)
29 | @ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {ComponentConfig.class})
30 | @TransactionConfiguration(defaultRollback = true)
31 | @Transactional
32 | @ActiveProfiles("local")
33 | public class TestJpaExpenseReportingService {
34 |
35 | @Inject
36 | private ExpenseReportingService service;
37 |
38 | private String purpose = "SFO face to face";
39 |
40 | @Test
41 | public void testCreatingAnExpenseReport() throws Throwable {
42 | Long reportId = this.service.createReport(this.purpose);
43 | assertNotNull(reportId);
44 | assertTrue(reportId > 0);
45 | }
46 |
47 | @Test
48 | public void testCreatingAnExpenseReportExpenses() throws Throwable {
49 | Long reportId = service.createReport(this.purpose);
50 | Collection chargeCollection = this.service.getEligibleCharges();
51 | List ids = new ArrayList(chargeCollection.size());
52 | for (EligibleCharge eligibleCharge : chargeCollection)
53 | ids.add(eligibleCharge.getId());
54 | Collection expenseCollection = service.createExpenses(reportId, ids);
55 | assertEquals(expenseCollection.size(), ids.size());
56 | assertEquals(expenseCollection.size(), service.getExpensesForExpenseReport(reportId).size());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/Bootstrap.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice;
17 |
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 | import org.apache.commons.logging.Log;
22 | import org.apache.commons.logging.LogFactory;
23 | import org.cloudfoundry.org.codehaus.jackson.JsonFactory;
24 | import org.cloudfoundry.org.codehaus.jackson.JsonParser;
25 | import org.cloudfoundry.org.codehaus.jackson.map.ObjectMapper;
26 |
27 | /**
28 | * TEMPORARY: Bootstrap to log the value of VCAP_SERVICES so that I can create a DB URL to the bound database service.
29 | *
30 | * @author wallsc
31 | */
32 | public class Bootstrap {
33 | private static final Log LOG = LogFactory.getLog(Bootstrap.class);
34 |
35 | public void go() throws Exception {
36 | String vcapServices = System.getenv("VCAP_SERVICES");
37 | LOG.debug("VCAP_SERVICES: " + vcapServices);
38 | // jdbc:postgresql://172.30.48.126:5432/d6f69ba9c3c6349ac830af2973e31b779
39 |
40 | // pull values out and construct JDBC URL
41 | Map credentials = getCredentialsMap(vcapServices);
42 | String dbName = (String) credentials.get("name");
43 | String host = (String) credentials.get("host");
44 | Integer port = (Integer) credentials.get("port");
45 | String username = (String) credentials.get("username");
46 | String password = (String) credentials.get("password");
47 |
48 | LOG.debug(" JDBC URL: jdbc:postgresql://" + host + ":" + port + "/" + dbName);
49 | LOG.debug(" DB USERNAME: " + username);
50 | LOG.debug(" DB PASSWORD: " + password);
51 | }
52 |
53 | public Map getCredentialsMap(String vcapServices) throws Exception {
54 | ObjectMapper mapper = new ObjectMapper();
55 | JsonFactory jsonFactory = mapper.getJsonFactory();
56 | JsonParser jsonParser = jsonFactory.createJsonParser(vcapServices);
57 | Map map = jsonParser.readValueAs(Map.class);
58 | List pgMap = (List) map.get("postgresql-9.0");
59 | Map dbMap = (Map) pgMap.get(0);
60 | Map credentialsMap = (Map) dbMap.get("credentials");
61 | return credentialsMap;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/server/api/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem ##
4 | @rem Gradle startup script for Windows ##
5 | @rem ##
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
12 | @rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512m
13 | @rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m
14 |
15 | set DIRNAME=%~dp0
16 | if "%DIRNAME%" == "" set DIRNAME=.\
17 |
18 | @rem Find java.exe
19 | set JAVA_EXE=java.exe
20 | if not defined JAVA_HOME goto init
21 |
22 | set JAVA_HOME=%JAVA_HOME:"=%
23 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
24 |
25 | if exist "%JAVA_EXE%" goto init
26 |
27 | echo.
28 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
29 | echo.
30 | echo Please set the JAVA_HOME variable in your environment to match the
31 | echo location of your Java installation.
32 | echo.
33 | goto end
34 |
35 | :init
36 | @rem Get command-line arguments, handling Windowz variants
37 |
38 | if not "%OS%" == "Windows_NT" goto win9xME_args
39 | if "%eval[2+2]" == "4" goto 4NT_args
40 |
41 | :win9xME_args
42 | @rem Slurp the command line arguments.
43 | set CMD_LINE_ARGS=
44 | set _SKIP=2
45 |
46 | :win9xME_args_slurp
47 | if "x%~1" == "x" goto execute
48 |
49 | set CMD_LINE_ARGS=%*
50 | goto execute
51 |
52 | :4NT_args
53 | @rem Get arguments from the 4NT Shell from JP Software
54 | set CMD_LINE_ARGS=%$
55 |
56 | :execute
57 | @rem Setup the command line
58 |
59 | set STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain
60 | set CLASSPATH=%DIRNAME%\gradle\wrapper\gradle-wrapper.jar
61 | set WRAPPER_PROPERTIES=%DIRNAME%\gradle\wrapper\gradle-wrapper.properties
62 |
63 | set GRADLE_OPTS=%JAVA_OPTS% %GRADLE_OPTS% -Dorg.gradle.wrapper.properties="%WRAPPER_PROPERTIES%"
64 |
65 | @rem Execute Gradle
66 | "%JAVA_EXE%" %GRADLE_OPTS% -classpath "%CLASSPATH%" %STARTER_MAIN_CLASS% %CMD_LINE_ARGS%
67 |
68 | :end
69 | @rem End local scope for the variables with windows NT shell
70 | if "%ERRORLEVEL%"=="0" goto mainEnd
71 |
72 | if not "%OS%"=="Windows_NT" echo 1 > nul | choice /n /c:1
73 |
74 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
75 | rem the _cmd.exe /c_ return code!
76 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit "%ERRORLEVEL%"
77 | exit /b "%ERRORLEVEL%"
78 |
79 | :mainEnd
80 | if "%OS%"=="Windows_NT" endlocal
81 |
82 | :omega
--------------------------------------------------------------------------------
/server/oauth/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem ##
4 | @rem Gradle startup script for Windows ##
5 | @rem ##
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
12 | @rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512m
13 | @rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m
14 |
15 | set DIRNAME=%~dp0
16 | if "%DIRNAME%" == "" set DIRNAME=.\
17 |
18 | @rem Find java.exe
19 | set JAVA_EXE=java.exe
20 | if not defined JAVA_HOME goto init
21 |
22 | set JAVA_HOME=%JAVA_HOME:"=%
23 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
24 |
25 | if exist "%JAVA_EXE%" goto init
26 |
27 | echo.
28 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
29 | echo.
30 | echo Please set the JAVA_HOME variable in your environment to match the
31 | echo location of your Java installation.
32 | echo.
33 | goto end
34 |
35 | :init
36 | @rem Get command-line arguments, handling Windowz variants
37 |
38 | if not "%OS%" == "Windows_NT" goto win9xME_args
39 | if "%eval[2+2]" == "4" goto 4NT_args
40 |
41 | :win9xME_args
42 | @rem Slurp the command line arguments.
43 | set CMD_LINE_ARGS=
44 | set _SKIP=2
45 |
46 | :win9xME_args_slurp
47 | if "x%~1" == "x" goto execute
48 |
49 | set CMD_LINE_ARGS=%*
50 | goto execute
51 |
52 | :4NT_args
53 | @rem Get arguments from the 4NT Shell from JP Software
54 | set CMD_LINE_ARGS=%$
55 |
56 | :execute
57 | @rem Setup the command line
58 |
59 | set STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain
60 | set CLASSPATH=%DIRNAME%\gradle\wrapper\gradle-wrapper.jar
61 | set WRAPPER_PROPERTIES=%DIRNAME%\gradle\wrapper\gradle-wrapper.properties
62 |
63 | set GRADLE_OPTS=%JAVA_OPTS% %GRADLE_OPTS% -Dorg.gradle.wrapper.properties="%WRAPPER_PROPERTIES%"
64 |
65 | @rem Execute Gradle
66 | "%JAVA_EXE%" %GRADLE_OPTS% -classpath "%CLASSPATH%" %STARTER_MAIN_CLASS% %CMD_LINE_ARGS%
67 |
68 | :end
69 | @rem End local scope for the variables with windows NT shell
70 | if "%ERRORLEVEL%"=="0" goto mainEnd
71 |
72 | if not "%OS%"=="Windows_NT" echo 1 > nul | choice /n /c:1
73 |
74 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
75 | rem the _cmd.exe /c_ return code!
76 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit "%ERRORLEVEL%"
77 | exit /b "%ERRORLEVEL%"
78 |
79 | :mainEnd
80 | if "%OS%"=="Windows_NT" endlocal
81 |
82 | :omega
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.config;
17 |
18 | import javax.inject.Inject;
19 | import javax.sql.DataSource;
20 |
21 | import org.springframework.context.annotation.Bean;
22 | import org.springframework.context.annotation.Configuration;
23 | import org.springframework.context.annotation.ImportResource;
24 | import org.springframework.security.crypto.encrypt.Encryptors;
25 | import org.springframework.security.crypto.encrypt.TextEncryptor;
26 | import org.springframework.security.oauth2.provider.ClientDetailsService;
27 | import org.springframework.security.oauth2.provider.JdbcClientDetailsService;
28 | import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
29 | import org.springframework.security.oauth2.provider.token.JdbcTokenStore;
30 | import org.springframework.security.oauth2.provider.token.RandomValueTokenServices;
31 | import org.springframework.security.oauth2.provider.token.TokenStore;
32 |
33 | @Configuration
34 | @ImportResource("classpath:com/springsource/oauthservice/config/security.xml")
35 | public class SecurityConfig {
36 |
37 | @Inject
38 | private DataSource dataSource;
39 |
40 | @Bean
41 | public ClientDetailsService clientDetails() {
42 | JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
43 | clientDetailsService.setSelectClientDetailsSql(SELECT_CLIENT_DETAILS_SQL);
44 | return clientDetailsService;
45 | }
46 |
47 | @Bean
48 | public AuthorizationServerTokenServices tokenServices() {
49 | RandomValueTokenServices tokenServices = new RandomValueTokenServices();
50 | TokenStore tokenStore = new JdbcTokenStore(dataSource);
51 | tokenServices.setTokenStore(tokenStore);
52 | tokenServices.setSupportRefreshToken(true);
53 | return tokenServices;
54 | }
55 |
56 | @Bean
57 | public TextEncryptor textEncryptor() {
58 | return Encryptors.noOpText();
59 | }
60 |
61 | private static final String SELECT_CLIENT_DETAILS_SQL = "select apiKey, resourceIds, secret, scope, grantTypes, redirectUrl, authorities from App where apiKey = ?";
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/CloudDataSourceConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import org.cloudfoundry.runtime.env.AbstractServiceInfo;
19 | import org.cloudfoundry.runtime.env.CloudEnvironment;
20 | import org.cloudfoundry.runtime.env.MongoServiceInfo;
21 | import org.cloudfoundry.runtime.env.RdbmsServiceInfo;
22 | import org.cloudfoundry.runtime.service.document.MongoServiceCreator;
23 | import org.cloudfoundry.runtime.service.relational.PostgresqlServiceCreator;
24 | import org.springframework.context.annotation.Bean;
25 | import org.springframework.context.annotation.Configuration;
26 | import org.springframework.context.annotation.Profile;
27 | import org.springframework.data.mongodb.MongoDbFactory;
28 | import org.springframework.data.mongodb.core.MongoTemplate;
29 | import org.springframework.util.Assert;
30 |
31 | import javax.sql.DataSource;
32 | import java.util.List;
33 |
34 | @Profile("cloud")
35 | @Configuration
36 | public class CloudDataSourceConfig implements DataSourceConfig {
37 |
38 | private CloudEnvironment cloudEnvironment = new CloudEnvironment();
39 |
40 | @Bean
41 | @Override
42 | public DataSource dataSource() throws Exception {
43 | RdbmsServiceInfo rdbmsServiceInfo = requireOneService("RDBMS", RdbmsServiceInfo.class);
44 | return new PostgresqlServiceCreator().createService(rdbmsServiceInfo);
45 | }
46 |
47 | @Bean
48 | @Override
49 | public MongoTemplate mongoTemplate() throws Exception {
50 | MongoServiceInfo mongoServiceInfo = requireOneService("MongoDB", MongoServiceInfo.class);
51 | MongoDbFactory mongoDbFactory = new MongoServiceCreator().createService(mongoServiceInfo);
52 | return new MongoTemplate(mongoDbFactory);
53 | }
54 |
55 | private T requireOneService(String serviceName, Class clazzOfT) throws Exception {
56 | String errMsg = "There must be only one %s service bound to this application. Currently, there are %s %s services.";
57 | List serviceInfoList = cloudEnvironment.getServiceInfos(clazzOfT);
58 | Assert.isTrue(serviceInfoList.size() == 1, String.format(errMsg, serviceName, serviceInfoList.size(), serviceName));
59 | return serviceInfoList.iterator().next();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/IdentityController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice;
17 |
18 | import java.security.Principal;
19 | import java.util.HashMap;
20 | import java.util.Map;
21 |
22 | import javax.inject.Inject;
23 |
24 | import org.springframework.security.oauth2.provider.OAuth2Authentication;
25 | import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
26 | import org.springframework.security.oauth2.provider.token.RandomValueTokenServices;
27 | import org.springframework.stereotype.Controller;
28 | import org.springframework.web.bind.annotation.RequestMapping;
29 | import org.springframework.web.bind.annotation.RequestMethod;
30 | import org.springframework.web.bind.annotation.ResponseBody;
31 | import org.springframework.web.context.request.NativeWebRequest;
32 |
33 | /**
34 | * Identity controller, allowing a client to fetch the identify of the authenticated user.
35 | * Intended to be secured by OAuth2, so that it will only fetch the identity when given a valid access token.
36 | * @author wallsc
37 | */
38 | @Controller
39 | public class IdentityController {
40 |
41 | private final AuthorizationServerTokenServices tokenServices;
42 |
43 | @Inject
44 | public IdentityController(AuthorizationServerTokenServices tokenServices) {
45 | this.tokenServices = tokenServices;
46 | }
47 |
48 | @RequestMapping(value="/me", method=RequestMethod.GET)
49 | public @ResponseBody Map getIdentity(Principal principal) {
50 | HashMap identityMap = new HashMap();
51 | identityMap.put("id", principal.getName());
52 | return identityMap;
53 | }
54 |
55 | @RequestMapping(value="/me/authentication", method=RequestMethod.GET)
56 | public @ResponseBody OAuth2Authentication getIdentity(NativeWebRequest request) {
57 | // TODO: Naively pull the access token from the header for now...if this works, refine this
58 | String authHeader = request.getHeader("Authorization");
59 | String accessToken = authHeader.split("\\s")[1];
60 | // TODO - unlike OAuth2ProviderTokenServices, AuthorizationServerTokenServices does not have a loadAuthentication method
61 | // - what should be used here instead?
62 | return ((RandomValueTokenServices) tokenServices).loadAuthentication(accessToken);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/server/oauth/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | contextClass
9 | org.springframework.web.context.support.AnnotationConfigWebApplicationContext
10 |
11 |
12 |
13 |
14 | contextConfigLocation
15 | com.springsource.oauthservice.config
16 |
17 |
18 |
19 |
20 | org.springframework.web.context.ContextLoaderListener
21 |
22 |
23 |
24 |
25 | appServlet
26 | org.springframework.web.servlet.DispatcherServlet
27 |
28 | contextConfigLocation
29 |
30 |
31 | 1
32 |
33 |
34 |
35 | appServlet
36 | /
37 |
38 |
39 |
40 |
41 | H2Console
42 | org.h2.server.web.WebServlet
43 |
44 | -webAllowOthers
45 | true
46 |
47 | 2
48 |
49 |
50 |
51 | H2Console
52 | /admin/h2/*
53 |
54 |
55 |
56 | springSecurityFilterChain
57 | org.springframework.web.filter.DelegatingFilterProxy
58 |
59 |
60 |
61 | springSecurityFilterChain
62 | /*
63 |
64 |
65 |
66 |
67 | hiddenHttpMethodFilter
68 | org.springframework.web.filter.HiddenHttpMethodFilter
69 |
70 |
71 |
72 | hiddenHttpMethodFilter
73 | /*
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/server/api/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'war'
2 | apply plugin: 'jetty'
3 | apply plugin: 'idea'
4 | apply plugin: 'eclipse'
5 |
6 | repositories {
7 | mavenRepo urls: 'https://repo.springsource.org/libs-snapshot'
8 | mavenCentral()
9 | }
10 |
11 | configurations {
12 | all*.exclude group: 'commons-logging', module: 'commons-logging'
13 | }
14 |
15 | springVersion = '3.1.0.RELEASE'
16 | cglibVersion = '2.2'
17 | logbackVersion = '0.9.29'
18 | javaxInjectVersion = '1'
19 | jspVersion = '2.1'
20 | jodaTimeVersion = '2.0'
21 | servletVersion = '2.5'
22 | slf4jVersion = '1.6.2'
23 | h2Version = '1.2.144'
24 | junitVersion = '4.7'
25 | jacksonVersion = '1.9.2'
26 | commonsFileuploadVersion = '1.2.2'
27 | commonsIoVersion = '2.0.1'
28 | commonsLangVersion = '2.5'
29 | hibernateVersion = '3.6.5.Final'
30 | cloudFoundryVersion = '0.8.1'
31 | s2OAuthVersion = '1.0.0.M5'
32 | thymeleafVersion = '1.1.4'
33 | springBatchVersion = '2.1.6.RELEASE'
34 | springIntegrationVersion = '2.1.0.RELEASE'
35 | mongoVersion = '1.0.0.RC1'
36 |
37 | dependencies {
38 | compile "joda-time:joda-time:$jodaTimeVersion"
39 | compile "org.hibernate:hibernate-entitymanager:$hibernateVersion"
40 | compile "com.h2database:h2:$h2Version"
41 | compile "cglib:cglib-nodep:$cglibVersion"
42 | compile "ch.qos.logback:logback-classic:$logbackVersion"
43 | compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
44 | compile "org.springframework:spring-context:$springVersion"
45 | compile "org.springframework:spring-webmvc:$springVersion"
46 | compile "org.springframework:spring-orm:$springVersion"
47 | compile "org.springframework.security.oauth:spring-security-oauth2:$s2OAuthVersion"
48 | compile "org.springframework.integration:spring-integration-jms:$springIntegrationVersion"
49 | compile "org.springframework.batch:spring-batch-core:$springBatchVersion"
50 | compile "javax.inject:javax.inject:$javaxInjectVersion"
51 | compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion"
52 | compile "commons-fileupload:commons-fileupload:$commonsFileuploadVersion"
53 | compile "commons-io:commons-io:$commonsIoVersion"
54 | compile "commons-lang:commons-lang:$commonsLangVersion"
55 | compile "org.cloudfoundry:cloudfoundry-runtime:$cloudFoundryVersion"
56 | compile "org.thymeleaf:thymeleaf-spring3:$thymeleafVersion"
57 | compile "postgresql:postgresql:8.3-603.jdbc3"
58 | compile "org.springframework.data:spring-data-mongodb:$mongoVersion"
59 | testCompile "org.springframework:spring-test:$springVersion"
60 | testCompile "junit:junit:$junitVersion"
61 | providedCompile "javax.servlet:servlet-api:$servletVersion"
62 | providedCompile "javax.servlet.jsp:jsp-api:$jspVersion"
63 | }
64 |
65 | sourceSets {
66 | main {
67 | resources {
68 | srcDirs = ['src/main/java', 'src/main/resources']
69 | }
70 | }
71 | test { resources { srcDirs = ['src/test/java'] } }
72 | }
73 |
74 | jettyRun.contextPath = '/html5expense'
75 |
76 | task wrapper(type: Wrapper) {
77 | gradleVersion = '1.0-milestone-3'
78 | }
79 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/Expense.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense;
17 |
18 |
19 | import javax.persistence.*;
20 | import java.math.BigDecimal;
21 | import java.util.Date;
22 |
23 | @Entity
24 | @Table(name = "EXPENSE")
25 | public class Expense {
26 |
27 | @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
28 | @Id
29 | private Integer id;
30 |
31 | @ManyToOne
32 | private ExpenseReport expenseReport;
33 |
34 | private Date expense_date;
35 |
36 | private String merchant;
37 |
38 | private String category;
39 |
40 | private BigDecimal amount;
41 |
42 | private Long chargeId;
43 |
44 | private String receipt;
45 |
46 | private String receiptExtension;
47 |
48 | private String flag;
49 |
50 |
51 | Expense(ExpenseReport er, Date expense_date, String merchant, String category, BigDecimal amount, Long chargeId) {
52 | this.expense_date = expense_date;
53 | this.expenseReport = er;
54 | this.merchant = merchant;
55 | this.category = category;
56 | this.amount = amount;
57 | this.chargeId = chargeId;
58 | }
59 |
60 | public Integer getId() {
61 | return id;
62 | }
63 |
64 | public boolean isFlagged() {
65 | return flag != null;
66 | }
67 |
68 | public void flag(String flag) {
69 | this.flag = flag;
70 | }
71 |
72 | public String getReceipt() {
73 | return receipt;
74 | }
75 |
76 | public String getReceiptExtension() {
77 | return this.receiptExtension;
78 | }
79 |
80 | public void attachReceipt(String receipt, String extension) {
81 | this.receipt = receipt;
82 | this.receiptExtension = extension;
83 |
84 | if (isFlagged() && this.flag.equals("receiptRequired")) {
85 | this.flag = null;
86 | }
87 | }
88 |
89 | public BigDecimal getAmount() {
90 | return amount;
91 | }
92 |
93 | public String getCategory() {
94 | return category;
95 | }
96 |
97 | public Long getChargeId() {
98 | return chargeId;
99 | }
100 |
101 | public Date getDate() {
102 | return expense_date;
103 | }
104 |
105 | public ExpenseReport getExpenseReport() {
106 | return expenseReport;
107 | }
108 |
109 | public String getFlag() {
110 | return flag;
111 | }
112 |
113 | public String getMerchant() {
114 | return merchant;
115 | }
116 | // hibernate
117 |
118 | Expense() {
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/server/api/src/main/webapp/assets/js/upload.js:
--------------------------------------------------------------------------------
1 | function setupUploader() {
2 |
3 | var dropbox = $('#dropbox'),
4 | message = $('.message', dropbox);
5 |
6 | dropbox.filedrop({
7 | // The name of the $_FILES entry:
8 | paramname:'pic',
9 |
10 | maxfiles: 20,
11 | maxfilesize: 2, // MBs
12 | url: 'post_file.php',
13 |
14 | uploadFinished:function(i, file, response) {
15 | $.data(file).addClass('done');
16 | // response is the JSON object that post_file.php returns
17 | },
18 |
19 | error: function(err, file) {
20 | switch (err) {
21 | case 'BrowserNotSupported':
22 | showMessage('Your browser does not support HTML5 file uploads!');
23 | break;
24 | case 'TooManyFiles':
25 | alert('Too many files! Please select 5 at most! (configurable)');
26 | break;
27 | case 'FileTooLarge':
28 | alert(file.name + ' is too large! Please upload files up to 2mb (configurable).');
29 | break;
30 | default:
31 | alert( err + "");
32 | break;
33 | }
34 | },
35 |
36 | // Called before each upload is started
37 | beforeEach: function(file) {
38 | if (!file.type.match(/^image\//)) {
39 | alert('Only images are allowed!');
40 |
41 | // Returning false will cause the
42 | // file to be rejected
43 | return false;
44 | }
45 | },
46 |
47 | uploadStarted:function(i, file, len) {
48 | createImage(file);
49 | },
50 |
51 | progressUpdated: function(i, file, progress) {
52 | $.data(file).find('.progress').width(progress);
53 | }
54 |
55 | });
56 |
57 | var template = '' +
58 | '
' +
59 | ' ' +
60 | ' ' +
61 | ' ' +
62 | '
' +
65 | '
';
66 |
67 |
68 | function createImage(file) {
69 |
70 | var preview = $(template),
71 | image = $('img', preview);
72 |
73 | var reader = new FileReader();
74 |
75 | image.width = 100;
76 | image.height = 100;
77 |
78 | reader.onload = function(e) {
79 |
80 | // e.target.result holds the DataURL which
81 | // can be used as a source of the image:
82 |
83 | image.attr('src', e.target.result);
84 | };
85 |
86 | // Reading the file as a DataURL. When finished,
87 | // this will trigger the onload function above:
88 | reader.readAsDataURL(file);
89 |
90 | message.hide();
91 | preview.appendTo(dropbox);
92 |
93 | // Associating a preview container
94 | // with the file, using jQuery's $.data():
95 |
96 | $.data(file, preview);
97 | }
98 |
99 | function showMessage(msg) {
100 | message.html(msg);
101 | }
102 | }
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/develop/AppController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.develop;
17 |
18 | import java.security.Principal;
19 | import java.util.List;
20 |
21 | import javax.inject.Inject;
22 | import javax.validation.Valid;
23 |
24 | import org.springframework.stereotype.Controller;
25 | import org.springframework.ui.Model;
26 | import org.springframework.validation.BindingResult;
27 | import org.springframework.web.bind.annotation.PathVariable;
28 | import org.springframework.web.bind.annotation.RequestMapping;
29 | import org.springframework.web.bind.annotation.RequestMethod;
30 |
31 | @Controller
32 | @RequestMapping("/develop")
33 | public class AppController {
34 |
35 | private final AppRepository appRepository;
36 |
37 | @Inject
38 | public AppController(AppRepository appRepository) {
39 | this.appRepository = appRepository;
40 | }
41 |
42 | @RequestMapping(value="/apps", method=RequestMethod.GET)
43 | public List list(Principal user) {
44 | return appRepository.findAppSummaries(user.getName());
45 | }
46 |
47 | @RequestMapping(value="/apps/new", method=RequestMethod.GET)
48 | public AppForm newForm() {
49 | return appRepository.getNewAppForm();
50 | }
51 |
52 | @RequestMapping(value="/apps", method=RequestMethod.POST)
53 | public String create(Principal user, @Valid AppForm form, BindingResult bindingResult) {
54 | if (bindingResult.hasErrors()) {
55 | return "develop/apps/new";
56 | }
57 | return "redirect:/develop/apps/" + appRepository.createApp(user.getName(), form);
58 | }
59 |
60 | @RequestMapping(value="/apps/{slug}", method=RequestMethod.GET)
61 | public String view(@PathVariable String slug, Principal user, Model model) {
62 | model.addAttribute(appRepository.findAppBySlug(user.getName(), slug));
63 | model.addAttribute("slug", slug);
64 | return "develop/apps/view";
65 | }
66 |
67 | @RequestMapping(value="/apps/{slug}", method=RequestMethod.DELETE)
68 | public String delete(@PathVariable String slug, Principal user) {
69 | appRepository.deleteApp(user.getName(), slug);
70 | return "redirect:/develop/apps";
71 | }
72 |
73 | @RequestMapping(value="/apps/edit/{slug}", method=RequestMethod.GET)
74 | public String editForm(@PathVariable String slug, Principal user, Model model) {
75 | model.addAttribute(appRepository.getAppForm(user.getName(), slug));
76 | model.addAttribute("slug", slug);
77 | return "develop/apps/edit";
78 | }
79 |
80 | @RequestMapping(value="/apps/{slug}", method=RequestMethod.PUT)
81 | public String update(@PathVariable String slug, @Valid AppForm form, BindingResult bindingResult, Principal user, Model model) {
82 | if (bindingResult.hasErrors()) {
83 | model.addAttribute("slug", slug);
84 | return "develop/apps/edit";
85 | }
86 | return "redirect:/develop/apps/" + appRepository.updateApp(user.getName(), slug, form);
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/services/utilities/MongoDbGridFsUtilities.java:
--------------------------------------------------------------------------------
1 | package com.springsource.html5expense.services.utilities;
2 |
3 | import com.mongodb.DB;
4 | import com.mongodb.DBObject;
5 | import com.mongodb.MongoException;
6 | import com.mongodb.gridfs.GridFS;
7 | import com.mongodb.gridfs.GridFSDBFile;
8 | import com.mongodb.gridfs.GridFSFile;
9 | import com.mongodb.gridfs.GridFSInputFile;
10 | import org.apache.commons.io.IOUtils;
11 | import org.springframework.dao.DataAccessException;
12 | import org.springframework.data.mongodb.core.DbCallback;
13 | import org.springframework.data.mongodb.core.MongoTemplate;
14 | import org.springframework.stereotype.Component;
15 | import org.springframework.util.Assert;
16 |
17 | import javax.inject.Inject;
18 | import java.io.InputStream;
19 |
20 | /**
21 | * Simple utility class to interface with MongoDB's GridFS
22 | * abstraction, which can be quite handy for storing files.
23 | *
24 | * NB: it's expected that more mature support will eventually be available in the Spring Data Mongo
25 | * project, so this is expected to be phased out, at some point.
26 | *
27 | * @author Josh Long
28 | */
29 | @Component
30 | public class MongoDbGridFsUtilities {
31 |
32 | private MongoTemplate mongoTemplate;
33 |
34 | @Inject
35 | public MongoDbGridFsUtilities(MongoTemplate mongoTemplate){
36 | this.mongoTemplate = mongoTemplate;
37 | }
38 |
39 | /**
40 | * A simple utility to write objects to the database
41 | *
42 | * @param bucket the name of the collection to store it into
43 | * @param content the content to write
44 | * @param filename the file name to use for the file (in practice, this could be any String)
45 | * @param metadata the metadata to associate with this information (it's optional)
46 | * @return a {@link GridFSFile} that represents the written data.
47 | */
48 | public GridFSFile write( final String bucket, final InputStream content, final String filename, final DBObject metadata) {
49 | Assert.notNull(content);
50 | Assert.hasText(filename);
51 | return mongoTemplate.execute(new DbCallback() {
52 | @Override
53 | public GridFSInputFile doInDB(DB db) throws MongoException, DataAccessException {
54 | GridFSInputFile file = gridFs(db, bucket).createFile(content, filename, true);
55 | file.setFilename(filename);
56 | if (null != metadata)
57 | file.setMetaData(metadata);
58 | file.save();
59 | IOUtils.closeQuietly(content);
60 | return file;
61 | }
62 | });
63 | }
64 |
65 | /**
66 | * Reads file data from MongoDB
67 | *
68 | * @param bucket the name of the collection to put the file
69 | * @param fileName the name of the file
70 | * @return an InputStream that the application can read from.
71 | */
72 | public InputStream read( final String bucket, final String fileName) {
73 | return mongoTemplate.executeInSession(new DbCallback() {
74 | @Override
75 | public InputStream doInDB(DB db) throws MongoException, DataAccessException {
76 | GridFS gridFS = gridFs(db, bucket);
77 | GridFSDBFile file = gridFS.findOne(fileName);
78 | return file.getInputStream();
79 | }
80 | });
81 | }
82 |
83 | private static GridFS gridFs(DB db, String bucket) {
84 | return new GridFS(db, bucket);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/client/ios/html5expense/Classes/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // html5expense
4 | //
5 | // Created by Roy Clarkson on 8/30/11.
6 | // Copyright VMware, Inc. 2011. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #ifdef PHONEGAP_FRAMEWORK
11 | #import
12 | #else
13 | #import "PhoneGapViewController.h"
14 | #endif
15 |
16 | @implementation AppDelegate
17 |
18 | @synthesize invokeString;
19 |
20 | - (id) init
21 | {
22 | /** If you need to do any extra app-specific initialization, you can do it here
23 | * -jm
24 | **/
25 | return [super init];
26 | }
27 |
28 | /**
29 | * This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up)
30 | */
31 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
32 | {
33 |
34 | NSArray *keyArray = [launchOptions allKeys];
35 | if ([launchOptions objectForKey:[keyArray objectAtIndex:0]]!=nil)
36 | {
37 | NSURL *url = [launchOptions objectForKey:[keyArray objectAtIndex:0]];
38 | self.invokeString = [url absoluteString];
39 | NSLog(@"html5expense launchOptions = %@",url);
40 | }
41 |
42 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
43 | }
44 |
45 | // this happens while we are running ( in the background, or from within our own app )
46 | // only valid if html5expense.plist specifies a protocol to handle
47 | - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
48 | {
49 | // must call super so all plugins will get the notification, and their handlers will be called
50 | // super also calls into javascript global function 'handleOpenURL'
51 | return [super application:application handleOpenURL:url];
52 | }
53 |
54 | -(id) getCommandInstance:(NSString*)className
55 | {
56 | /** You can catch your own commands here, if you wanted to extend the gap: protocol, or add your
57 | * own app specific protocol to it. -jm
58 | **/
59 | return [super getCommandInstance:className];
60 | }
61 |
62 | /**
63 | Called when the webview finishes loading. This stops the activity view and closes the imageview
64 | */
65 | - (void)webViewDidFinishLoad:(UIWebView *)theWebView
66 | {
67 | // only valid if html5expense.plist specifies a protocol to handle
68 | if(self.invokeString)
69 | {
70 | // this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready
71 | NSString* jsString = [NSString stringWithFormat:@"var invokeString = \"%@\";", self.invokeString];
72 | [theWebView stringByEvaluatingJavaScriptFromString:jsString];
73 | }
74 | return [ super webViewDidFinishLoad:theWebView ];
75 | }
76 |
77 | - (void)webViewDidStartLoad:(UIWebView *)theWebView
78 | {
79 | return [ super webViewDidStartLoad:theWebView ];
80 | }
81 |
82 | /**
83 | * Fail Loading With Error
84 | * Error - If the webpage failed to load display an error with the reason.
85 | */
86 | - (void)webView:(UIWebView *)theWebView didFailLoadWithError:(NSError *)error
87 | {
88 | return [ super webView:theWebView didFailLoadWithError:error ];
89 | }
90 |
91 | /**
92 | * Start Loading Request
93 | * This is where most of the magic happens... We take the request(s) and process the response.
94 | * From here we can re direct links and other protocalls to different internal methods.
95 | */
96 | - (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
97 | {
98 | return [ super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType ];
99 | }
100 |
101 |
102 | - (BOOL) execute:(InvokedUrlCommand*)command
103 | {
104 | return [ super execute:command];
105 | }
106 |
107 | - (void)dealloc
108 | {
109 | [ super dealloc ];
110 | }
111 |
112 | @end
113 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/ExpenseReportingService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense;
17 |
18 | import java.io.InputStream;
19 | import java.math.BigDecimal;
20 | import java.util.Collection;
21 | import java.util.Date;
22 | import java.util.List;
23 |
24 |
25 | /**
26 | * Manages user expense reports.
27 | *
28 | * @author Keith Donald
29 | * @author Josh Long
30 | */
31 | public interface ExpenseReportingService {
32 |
33 | void deleteExpenseReport(Long expenseReportId);
34 |
35 | InputStream retrieveReceipt(Integer expenseId);
36 |
37 | void updateExpenseReportPurpose(Long reportId, String title);
38 |
39 | Collection getExpensesForExpenseReport(Long reportId);
40 |
41 | /**
42 | * Responsible for installing new {@link EligibleCharge}s into the database
43 | */
44 | EligibleCharge createEligibleCharge(Date date, String merchant, String category, BigDecimal amt);
45 |
46 | /**
47 | * Creates a new expense report.
48 | *
49 | * @param purpose the purpose for this report, e.g., "Palo Alto Face to Face Meeting"
50 | * @return the unique ID of the expense report
51 | */
52 | Long createReport(String purpose);
53 |
54 | void restoreEligibleCharges(List expenseIds);
55 |
56 | /**
57 | * Retrieves the charges that are eligible to be expensed.
58 | * The user is expected to add one or more of these charges to the report.
59 | *
60 | * @return the list of eligible charges
61 | */
62 | Collection getEligibleCharges();
63 |
64 | /**
65 | * Adds the selected charges to the expense report.
66 | * Creates and returns a new expense for each charge.
67 | *
68 | * @param reportId the expense report id
69 | * @param chargeIds the eligible charge ids
70 | * @return an expense for each charge
71 | */
72 | Collection createExpenses(Long reportId, List chargeIds);
73 |
74 | /**
75 | * Attach a receipt to an expense.
76 | *
77 | * @param reportId the expense report id
78 | * @param receiptBytes the receipt data as a byte array
79 | * @param ext the extension of the uploaded media
80 | * @return a pointer to the receipt
81 | */
82 | String attachReceipt(Long reportId, Integer expenseId, String ext, byte[] receiptBytes);
83 |
84 | /**
85 | * Submit the expense report for approval.
86 | *
87 | * @param reportId the id of the report to file
88 | */
89 | void submitReport(Long reportId);
90 |
91 | /**
92 | * Returns all the expense reports the user has open.
93 | * An open report is not under review and is not closed.
94 | * It can be edited by the user and {@link #submitReport(Long) submitted}.
95 | *
96 | * @return the user's open expense reports
97 | */
98 | List getOpenReports();
99 |
100 | /**
101 | * Returns all the expense reports the user has submitted.
102 | * A submitted report is under review or approved.
103 | *
104 | * @return the user's submitted expense reports
105 | */
106 | List getSubmittedReports();
107 |
108 | ExpenseReport getExpenseReport(Long reportId);
109 |
110 | Expense getExpense(Integer expenseId);
111 | }
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/security/EndpointTokenServices.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.security;
17 |
18 | import org.codehaus.jackson.map.ObjectMapper;
19 | import org.springframework.http.HttpEntity;
20 | import org.springframework.http.HttpHeaders;
21 | import org.springframework.http.HttpMethod;
22 | import org.springframework.http.ResponseEntity;
23 | import org.springframework.http.converter.HttpMessageConverter;
24 | import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
25 | import org.springframework.security.core.AuthenticationException;
26 | import org.springframework.security.oauth2.common.OAuth2AccessToken;
27 | import org.springframework.security.oauth2.provider.OAuth2Authentication;
28 | import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
29 | import org.springframework.web.client.RestTemplate;
30 |
31 | import java.util.List;
32 | import java.util.Set;
33 |
34 | /**
35 | * Implementation of OAuth2ProviderTokenServices that loads authentication via the OAuth service's authentication endpoint.
36 | *
37 | * @author wallsc
38 | */
39 | public class EndpointTokenServices implements ResourceServerTokenServices {
40 |
41 | private final String oauthAuthenticationUrl;
42 |
43 | private final RestTemplate restTemplate;
44 |
45 | public EndpointTokenServices(String oauthAuthenticationUrl) {
46 | this.oauthAuthenticationUrl = oauthAuthenticationUrl;
47 |
48 | this.restTemplate = new RestTemplate();
49 | List> messageConverters = restTemplate.getMessageConverters();
50 | for (HttpMessageConverter> httpMessageConverter : messageConverters) {
51 | if (httpMessageConverter.getClass().equals(MappingJacksonHttpMessageConverter.class)) {
52 | MappingJacksonHttpMessageConverter jsonConverter = (MappingJacksonHttpMessageConverter) httpMessageConverter;
53 | ObjectMapper mapper = new ObjectMapper();
54 | mapper.getDeserializationConfig().addMixInAnnotations(OAuth2Authentication.class, OAuth2AuthenticationMixin.class);
55 | jsonConverter.setObjectMapper(mapper);
56 | }
57 | }
58 | }
59 |
60 |
61 | public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException {
62 | // TODO: Probably should catch REST client exceptions and rethrow as AuthenticationException
63 | HttpHeaders headers = new HttpHeaders();
64 | headers.add("Authorization", "Bearer " + accessToken);
65 | HttpEntity requestEntity = new HttpEntity(headers);
66 | ResponseEntity response = restTemplate.exchange(oauthAuthenticationUrl, HttpMethod.GET, requestEntity, OAuth2Authentication.class);
67 |
68 | OAuth2Authentication oauth2Authentication = response.getBody();
69 | return oauth2Authentication;
70 | }
71 |
72 | public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
73 | throw new UnsupportedOperationException("Can't create an access token through this implementation.");
74 | }
75 |
76 | public OAuth2AccessToken refreshAccessToken(String refreshToken, Set scope) throws AuthenticationException {
77 | throw new UnsupportedOperationException("Can't refresh an access token through this implementation.");
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/ComponentConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import com.springsource.html5expense.EligibleCharge;
19 | import com.springsource.html5expense.services.JpaExpenseReportingService;
20 | import org.springframework.context.annotation.Bean;
21 | import org.springframework.context.annotation.ComponentScan;
22 | import org.springframework.context.annotation.Configuration;
23 | import org.springframework.context.annotation.Import;
24 | import org.springframework.core.io.ClassPathResource;
25 | import org.springframework.jdbc.datasource.init.DataSourceInitializer;
26 | import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
27 | import org.springframework.orm.jpa.JpaTransactionManager;
28 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
29 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
30 | import org.springframework.transaction.PlatformTransactionManager;
31 | import org.springframework.transaction.annotation.EnableTransactionManagement;
32 |
33 | import javax.inject.Inject;
34 | import javax.persistence.EntityManagerFactory;
35 | import java.util.HashMap;
36 | import java.util.Map;
37 |
38 | /**
39 | * Configuration for application @Components such as @Services, @Repositories, and @Controllers.
40 | * Loads externalized property values required to configure the various application properties.
41 | * Not much else here, as we rely on @Component scanning in conjunction with @Inject by-type autowiring.
42 | *
43 | * @author Keith Donald
44 | * @author Josh Long
45 | * @author Roy Clarkson
46 | */
47 | @Configuration
48 | @EnableTransactionManagement
49 | @Import({LocalDataSourceConfig.class, CloudDataSourceConfig.class/*, SecurityConfig.class*/})
50 | @ComponentScan(basePackageClasses = JpaExpenseReportingService.class)
51 | public class ComponentConfig {
52 |
53 | @Inject
54 | private DataSourceConfig dataSourceConfig;
55 |
56 | @Bean
57 | public DataSourceInitializer dataSourceInitializer() throws Throwable {
58 | ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
59 | databasePopulator.setContinueOnError(true);
60 | databasePopulator.setIgnoreFailedDrops(true);
61 | databasePopulator.addScript(new ClassPathResource("/setup/demo-data.sql"));
62 |
63 | DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
64 | dataSourceInitializer.setDataSource(this.dataSourceConfig.dataSource());
65 | dataSourceInitializer.setEnabled(true);
66 | dataSourceInitializer.setDatabasePopulator(databasePopulator);
67 |
68 | return dataSourceInitializer;
69 | }
70 |
71 | @Bean
72 | public PlatformTransactionManager transactionManager() throws Exception {
73 | EntityManagerFactory emf = entityManagerFactory().getObject();
74 | return new JpaTransactionManager(emf);
75 | }
76 |
77 | @Bean
78 | public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws Exception {
79 |
80 | Map props = new HashMap();
81 | props.put("hibernate.hbm2ddl.auto", "create-update");
82 |
83 | LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
84 | emfb.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
85 | emfb.setDataSource(dataSourceConfig.dataSource());
86 | emfb.setJpaPropertyMap(props);
87 | emfb.setPackagesToScan(EligibleCharge.class.getPackage().getName());
88 |
89 | return emfb;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # HTML5 EXPENSE CLIENT #
2 |
3 | Expense reporting reference app demonstrating HTML5 and cross-platform mobile
4 |
5 | ## Environment Setup ##
6 |
7 | ### iOS ###
8 |
9 | Building iOS projects with PhoneGap requires Apple OS X.
10 |
11 | #### Xcode ####
12 |
13 | Install Xcode from the [Mac App Store](https://itunes.apple.com/us/app/xcode/id448457090?mt=12).
14 |
15 | #### PhoneGap ####
16 |
17 | The PhoneGap installer will create a new Xcode project template, which you can use to create new iOS PhoneGap projects. The HTML5 Expense client was created with this template.
18 |
19 | 1. Download version 1.3.0 or newer of [PhoneGap](https://www.phonegap.com/).
20 | 2. Unzip the PhoneGap package.
21 | 3. From the iOS directory, open the PhoneGap-1.3.0.dmg disk image.
22 | 4. Double click the PhoneGap-1.3.0.pkg from within the disk image contents to install.
23 |
24 | ### Android ###
25 |
26 | Building Android projects is supported on Windows, OS X, and Linux.
27 |
28 | #### Eclipse ####
29 |
30 | Download and install [Eclipse](https://www.eclipse.org/downloads/). The [SpringSource Tool Suite](https://www.springsource.com/landing/best-development-tool-enterprise-java) also works quite nicely.
31 |
32 | #### Android SDK ####
33 |
34 | Download and install the [Android SDK](https://developer.android.com/sdk/index.html).
35 |
36 | #### Android Development Tools (ADT) Plugin for Eclipse ####
37 |
38 | Install the [ADT Plugin](https://developer.android.com/sdk/eclipse-adt.html#installing) in Eclipse.
39 |
40 | #### PhoneGap ####
41 |
42 | PhoneGap does not provide an installer for Android projects, however when you create a new project you simply deploy the PhoneGap JAR and JavaScript files into your new project. The HTML5 Expense Android client project directory already has these items included.
43 |
44 | ## Project Structure ##
45 |
46 | The 'shared/www' directory contains the HTML, CSS, and JavaScript that is being used within the iOS and Android Phonegap projects. A symbolic link (symlink) is used within each project to point back to this folder. In the iOS project, the symlink is 'ios/www'. In the Android project it is located at 'android/assets/www'. Symlinks are supported in Mac OS X, Linux, Windows Vista, and Windows 7. This PhoneGap [wiki page](https://github.com/phonegap/phonegap/wiki) describes this solution for shared web resources across device types.
47 |
48 | ## Open the Client Projects ##
49 |
50 | This section describes how to open the iOS and Android projects within Xcode and Eclipse respectively.
51 |
52 | ### iOS ###
53 |
54 | After you have completed the environment setup for Xcode, you can open the iOS client project.
55 |
56 | #### Open Project in Xcode ####
57 |
58 | 1. Open Xcode.
59 | 2. Select File -> Open, or click the Open Other button on the welcome screen.
60 | 3. In the Open dialog, browse to the html5expense/client/ios directory and click Open.
61 |
62 | The project should now build successfully
63 |
64 | ### Android ###
65 |
66 | After you have completed the environment setup for Eclipse, you can open the Android client project.
67 |
68 | #### Import Project into Eclipse ####
69 |
70 | Follow these steps to import the Android client project into Eclipse.
71 |
72 | 1. From the File menu, select New -> Project...
73 | 2. From the New Project dialog, select Android -> Android Project, and click Next. NOTE: if Android is not available, the ADT Plugin for Eclipse is not installed properly.
74 | 3. From the New Android Project dialog, enter html5expense in the Project name field.
75 | 4. Within the Contents section, select the Create project from existing source radio button.
76 | 5. In the Open dialog, browse to the html5expense/client/android directory and click Open.
77 | 6. All the remaining fields in the New Android Project dialog should now be populated.
78 | 7. Click Finish.
79 |
80 | #### Configure Build Path ####
81 |
82 | That completes the steps to import the project. Note the html5expense project listed in the Eclipse Package Explorer on the left side of the screen. Also note the small red X over the project. Html5expense will not build without another small modification. Follow these steps to complete the project setup in Eclipse.
83 |
84 | 1. Right click (Command click on OS X) the html5expense project in Eclipse.
85 | 2. Navigate to Build Path -> Configure Build Path.
86 | 3. Select the Libraries tab.
87 | 4. Click Add JARs...
88 | 5. In the JAR Selection dialog, navigate to html5expense/libs/phonegap-1.3.0.jar and click OK. The PhoneGap jar should now be listed in the "JARs and class folders on the build path" section.
89 | 6. Click OK.
90 |
91 | The project should now build successfully, and the red X should disappear from the project listing.
92 |
93 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/WebConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import com.springsource.html5expense.controllers.ExpenseReportingApiController;
19 | import org.springframework.beans.factory.annotation.Value;
20 | import org.springframework.context.MessageSource;
21 | import org.springframework.context.annotation.*;
22 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
23 | import org.springframework.context.support.ResourceBundleMessageSource;
24 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
25 | import org.springframework.web.multipart.commons.CommonsMultipartResolver;
26 | import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
27 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
28 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
29 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
30 | import org.thymeleaf.TemplateMode;
31 | import org.thymeleaf.spring3.SpringTemplateEngine;
32 | import org.thymeleaf.spring3.view.ThymeleafViewResolver;
33 | import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
34 |
35 | import java.util.List;
36 |
37 | @Configuration
38 | @ComponentScan(basePackageClasses = ExpenseReportingApiController.class)
39 | @Import(ComponentConfig.class)
40 | @PropertySource("/config.properties")
41 | @EnableWebMvc
42 | public class WebConfig extends WebMvcConfigurerAdapter {
43 |
44 | @Value("${debug}")
45 | private boolean debug;
46 |
47 | private int maxUploadSizeInMb = 20 * 1024 * 1024;
48 |
49 | @Bean(name = "multipartResolver")
50 | public CommonsMultipartResolver commonsMultipartResolver() {
51 | CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
52 | commonsMultipartResolver.setMaxUploadSize(maxUploadSizeInMb);
53 | return commonsMultipartResolver;
54 | }
55 |
56 | @Override
57 | public void configureDefaultServletHandling(
58 | DefaultServletHandlerConfigurer configurer) {
59 | configurer.enable();
60 | }
61 |
62 | @Override
63 | public void addViewControllers(ViewControllerRegistry registry) {
64 | registry.addViewController("/").setViewName("receipts");
65 | }
66 |
67 | @Bean
68 | public ThymeleafViewResolver viewResolver() {
69 | ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
70 | thymeleafViewResolver.setTemplateEngine(this.templateEngine());
71 | return thymeleafViewResolver;
72 | }
73 |
74 | @Bean
75 | public MessageSource messageSource() {
76 | String[] baseNames = "messages,errors".split(",");
77 | ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
78 | resourceBundleMessageSource.setBasenames(baseNames);
79 | return resourceBundleMessageSource;
80 | }
81 |
82 | @Bean
83 | public ServletContextTemplateResolver servletContextTemplateResolver() {
84 | ServletContextTemplateResolver servletContextTemplateResolver = new ServletContextTemplateResolver();
85 | servletContextTemplateResolver.setPrefix("/WEB-INF/views/");
86 | servletContextTemplateResolver.setCacheable(!debug);
87 | servletContextTemplateResolver.setSuffix(".xhtml");
88 | servletContextTemplateResolver.setTemplateMode(TemplateMode.HTML5);
89 | return servletContextTemplateResolver;
90 | }
91 |
92 | @Bean
93 | public SpringTemplateEngine templateEngine() {
94 | SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
95 | springTemplateEngine.setTemplateResolver(this.servletContextTemplateResolver());
96 | return springTemplateEngine;
97 | }
98 |
99 | @Bean
100 | public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
101 | return new PropertySourcesPlaceholderConfigurer();
102 | }
103 | }
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/ExpenseReport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense;
17 |
18 | import com.springsource.html5expense.services.Flag;
19 |
20 | import javax.persistence.*;
21 | import java.math.BigDecimal;
22 | import java.util.ArrayList;
23 | import java.util.Date;
24 | import java.util.List;
25 |
26 | @Entity
27 | @Table(name = "EXPENSE_REPORT")
28 | public class ExpenseReport {
29 |
30 | @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
31 | @Id
32 | private Long id;
33 |
34 | private String purpose;
35 |
36 | @OneToMany(mappedBy = "expenseReport")
37 | private List expenses = new ArrayList();
38 |
39 | private BigDecimal receiptRequiredAmount = new BigDecimal("25.00");
40 |
41 | @Enumerated(EnumType.STRING)
42 | private State state = State.NEW;
43 |
44 | public void setPurpose(String purpose) {
45 | this.purpose = purpose;
46 | }
47 |
48 | public State getState() {
49 | return this.state;
50 | }
51 |
52 | public ExpenseReport(String purpose) {
53 | this.purpose = purpose;
54 | }
55 |
56 | public ExpenseReport(Long id, String purpose) {
57 | this.id = id;
58 | this.purpose = purpose;
59 | }
60 |
61 | public Long getId() {
62 | return id;
63 | }
64 |
65 | public String getPurpose() {
66 | return purpose;
67 | }
68 |
69 | public boolean isOpen() {
70 | return state == State.NEW || state == State.REJECTED;
71 | }
72 |
73 | public boolean isSubmitted() {
74 | return state == State.APPROVED || state == State.IN_REVIEW;
75 | }
76 |
77 | public Expense createExpense(EligibleCharge charge) {
78 | return this.createExpense(charge.getDate(), charge.getMerchant(),
79 | charge.getCategory(), charge.getAmount(), charge.getI());
80 | }
81 |
82 | private Expense createExpense(Date date, String merchant, String category, BigDecimal amount, Long chargeId) {
83 | assertOpen();
84 | Expense expense = new Expense(this, date, merchant, category, amount, chargeId);
85 | if (expense.getAmount().compareTo(receiptRequiredAmount) == 1)
86 | expense.flag("receiptRequired");
87 | this.expenses.add(expense);
88 | return expense;
89 | }
90 |
91 | public void attachReceipt(Integer expenseId, String receipt, String key) {
92 | assertOpen();
93 | getExpense(expenseId).attachReceipt(receipt, key);
94 | }
95 |
96 | public void markInReview() {
97 | assertOpen();
98 | if (isFlagged()) {
99 | throw new IllegalStateException("Report is flagged");
100 | }
101 | this.state = State.IN_REVIEW;
102 | }
103 |
104 | public void markRejected(List flags) {
105 | assertInReview();
106 | for (Flag flag : flags) {
107 | getExpense(flag.getExpenseId()).flag(flag.getValue());
108 | }
109 | this.state = State.REJECTED;
110 | }
111 |
112 | public void markApproved() {
113 | assertInReview();
114 | this.state = State.APPROVED;
115 | }
116 |
117 | private void assertOpen() {
118 | if (!isOpen()) {
119 | throw new IllegalStateException("Report not open");
120 | }
121 | }
122 |
123 | private void assertInReview() {
124 | if (state != State.IN_REVIEW) {
125 | throw new IllegalStateException("Report not in review");
126 | }
127 | }
128 |
129 | private boolean isFlagged() {
130 | for (Expense expense : expenses) {
131 | if (expense.isFlagged()) {
132 | return true;
133 | }
134 | }
135 | return false;
136 | }
137 |
138 | private Expense getExpense(Integer id) {
139 | for (Expense expense : expenses) {
140 | if (expense.getId().equals(id)) {
141 | return expense;
142 | }
143 | }
144 | throw new IllegalArgumentException("No such expense");
145 | }
146 |
147 | // Hibernate
148 |
149 | ExpenseReport() {
150 | }
151 |
152 | }
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/security/OAuth2AuthenticationMixin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.security;
17 |
18 | import com.springsource.html5expense.security.OAuth2AuthenticationMixin.OAuth2AuthenticationDeserializer;
19 | import org.codehaus.jackson.JsonNode;
20 | import org.codehaus.jackson.JsonParser;
21 | import org.codehaus.jackson.JsonProcessingException;
22 | import org.codehaus.jackson.annotate.JsonIgnoreProperties;
23 | import org.codehaus.jackson.map.DeserializationContext;
24 | import org.codehaus.jackson.map.JsonDeserializer;
25 | import org.codehaus.jackson.map.annotate.JsonDeserialize;
26 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
27 | import org.springframework.security.core.GrantedAuthority;
28 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
29 | import org.springframework.security.oauth2.provider.ClientToken;
30 | import org.springframework.security.oauth2.provider.OAuth2Authentication;
31 |
32 | import java.io.IOException;
33 | import java.util.HashSet;
34 | import java.util.Iterator;
35 | import java.util.Set;
36 |
37 | @JsonIgnoreProperties(ignoreUnknown = true)
38 | @JsonDeserialize(using = OAuth2AuthenticationDeserializer.class)
39 | public class OAuth2AuthenticationMixin {
40 |
41 | public static class OAuth2AuthenticationDeserializer extends JsonDeserializer {
42 | @Override
43 | public OAuth2Authentication deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
44 | JsonNode tree = jp.readValueAsTree();
45 | ClientToken clientAuthentication = deserializeClientAuthentication(tree);
46 | UsernamePasswordAuthenticationToken userAuthentication = deserializeUserAuthentication(tree);
47 | OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(clientAuthentication, userAuthentication);
48 | oAuth2Authentication.setAuthenticated(true);
49 | return oAuth2Authentication;
50 | }
51 |
52 | private ClientToken deserializeClientAuthentication(JsonNode treeNode) {
53 | JsonNode clientAuthenticationNode = treeNode.get("clientAuthentication");
54 | String clientId = clientAuthenticationNode.get("clientId").getValueAsText();
55 | JsonNode resourceIdsNode = clientAuthenticationNode.get("resourceIds");
56 | Set resourceIds = new HashSet(resourceIdsNode.size());
57 | for (Iterator resourceIdsIt = resourceIdsNode.getElements(); resourceIdsIt.hasNext(); ) {
58 | resourceIds.add(resourceIdsIt.next().getValueAsText());
59 | }
60 | String clientSecret = clientAuthenticationNode.get("clientSecret").getValueAsText();
61 |
62 | JsonNode scopeNode = clientAuthenticationNode.get("scope");
63 | Set scope = new HashSet(scopeNode.size());
64 | for (Iterator scopeIt = scopeNode.getElements(); scopeIt.hasNext(); ) {
65 | scope.add(scopeIt.next().getValueAsText());
66 | }
67 | Set authorities = getAuthorities(clientAuthenticationNode);
68 | return new ClientToken(clientId, resourceIds, clientSecret, scope, authorities);
69 |
70 | }
71 |
72 | private UsernamePasswordAuthenticationToken deserializeUserAuthentication(JsonNode treeNode) {
73 | JsonNode userAuthenticationNode = treeNode.get("userAuthentication");
74 | String username = userAuthenticationNode.get("principal").get("username").getValueAsText();
75 | Set authorities = getAuthorities(userAuthenticationNode);
76 | UsernamePasswordAuthenticationToken userAuthentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
77 | return userAuthentication;
78 | }
79 |
80 | private Set getAuthorities(JsonNode parentNode) {
81 | JsonNode authoritiesNode = parentNode.get("authorities");
82 | Set authorities = new HashSet(authoritiesNode.size());
83 | for (Iterator authoritiesIt = authoritiesNode.getElements(); authoritiesIt.hasNext(); ) {
84 | JsonNode authorityNode = authoritiesIt.next();
85 | authorities.add(new SimpleGrantedAuthority(authorityNode.get("authority").getValueAsText()));
86 | }
87 | return authorities;
88 | }
89 |
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/client/shared/www/jquery.json-2.3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jQuery JSON Plugin
3 | * version: 2.3 (2011-09-17)
4 | *
5 | * This document is licensed as free software under the terms of the
6 | * MIT License: https://www.opensource.org/licenses/mit-license.php
7 | *
8 | * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
9 | * website's https://www.json.org/json2.js, which proclaims:
10 | * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
11 | * I uphold.
12 | *
13 | * It is also influenced heavily by MochiKit's serializeJSON, which is
14 | * copyrighted 2005 by Bob Ippolito.
15 | */
16 |
17 | (function( $ ) {
18 |
19 | var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
20 | meta = {
21 | '\b': '\\b',
22 | '\t': '\\t',
23 | '\n': '\\n',
24 | '\f': '\\f',
25 | '\r': '\\r',
26 | '"' : '\\"',
27 | '\\': '\\\\'
28 | };
29 |
30 | /**
31 | * jQuery.toJSON
32 | * Converts the given argument into a JSON respresentation.
33 | *
34 | * @param o {Mixed} The json-serializble *thing* to be converted
35 | *
36 | * If an object has a toJSON prototype, that will be used to get the representation.
37 | * Non-integer/string keys are skipped in the object, as are keys that point to a
38 | * function.
39 | *
40 | */
41 | $.toJSON = typeof JSON === 'object' && JSON.stringify
42 | ? JSON.stringify
43 | : function( o ) {
44 |
45 | if ( o === null ) {
46 | return 'null';
47 | }
48 |
49 | var type = typeof o;
50 |
51 | if ( type === 'undefined' ) {
52 | return undefined;
53 | }
54 | if ( type === 'number' || type === 'boolean' ) {
55 | return '' + o;
56 | }
57 | if ( type === 'string') {
58 | return $.quoteString( o );
59 | }
60 | if ( type === 'object' ) {
61 | if ( typeof o.toJSON === 'function' ) {
62 | return $.toJSON( o.toJSON() );
63 | }
64 | if ( o.constructor === Date ) {
65 | var month = o.getUTCMonth() + 1,
66 | day = o.getUTCDate(),
67 | year = o.getUTCFullYear(),
68 | hours = o.getUTCHours(),
69 | minutes = o.getUTCMinutes(),
70 | seconds = o.getUTCSeconds(),
71 | milli = o.getUTCMilliseconds();
72 |
73 | if ( month < 10 ) {
74 | month = '0' + month;
75 | }
76 | if ( day < 10 ) {
77 | day = '0' + day;
78 | }
79 | if ( hours < 10 ) {
80 | hours = '0' + hours;
81 | }
82 | if ( minutes < 10 ) {
83 | minutes = '0' + minutes;
84 | }
85 | if ( seconds < 10 ) {
86 | seconds = '0' + seconds;
87 | }
88 | if ( milli < 100 ) {
89 | milli = '0' + milli;
90 | }
91 | if ( milli < 10 ) {
92 | milli = '0' + milli;
93 | }
94 | return '"' + year + '-' + month + '-' + day + 'T' +
95 | hours + ':' + minutes + ':' + seconds +
96 | '.' + milli + 'Z"';
97 | }
98 | if ( o.constructor === Array ) {
99 | var ret = [];
100 | for ( var i = 0; i < o.length; i++ ) {
101 | ret.push( $.toJSON( o[i] ) || 'null' );
102 | }
103 | return '[' + ret.join(',') + ']';
104 | }
105 | var name,
106 | val,
107 | pairs = [];
108 | for ( var k in o ) {
109 | type = typeof k;
110 | if ( type === 'number' ) {
111 | name = '"' + k + '"';
112 | } else if (type === 'string') {
113 | name = $.quoteString(k);
114 | } else {
115 | // Keys must be numerical or string. Skip others
116 | continue;
117 | }
118 | type = typeof o[k];
119 |
120 | if ( type === 'function' || type === 'undefined' ) {
121 | // Invalid values like these return undefined
122 | // from toJSON, however those object members
123 | // shouldn't be included in the JSON string at all.
124 | continue;
125 | }
126 | val = $.toJSON( o[k] );
127 | pairs.push( name + ':' + val );
128 | }
129 | return '{' + pairs.join( ',' ) + '}';
130 | }
131 | };
132 |
133 | /**
134 | * jQuery.evalJSON
135 | * Evaluates a given piece of json source.
136 | *
137 | * @param src {String}
138 | */
139 | $.evalJSON = typeof JSON === 'object' && JSON.parse
140 | ? JSON.parse
141 | : function( src ) {
142 | return eval('(' + src + ')');
143 | };
144 |
145 | /**
146 | * jQuery.secureEvalJSON
147 | * Evals JSON in a way that is *more* secure.
148 | *
149 | * @param src {String}
150 | */
151 | $.secureEvalJSON = typeof JSON === 'object' && JSON.parse
152 | ? JSON.parse
153 | : function( src ) {
154 |
155 | var filtered =
156 | src
157 | .replace( /\\["\\\/bfnrtu]/g, '@' )
158 | .replace( /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
159 | .replace( /(?:^|:|,)(?:\s*\[)+/g, '');
160 |
161 | if ( /^[\],:{}\s]*$/.test( filtered ) ) {
162 | return eval( '(' + src + ')' );
163 | } else {
164 | throw new SyntaxError( 'Error parsing JSON, source is not valid.' );
165 | }
166 | };
167 |
168 | /**
169 | * jQuery.quoteString
170 | * Returns a string-repr of a string, escaping quotes intelligently.
171 | * Mostly a support function for toJSON.
172 | * Examples:
173 | * >>> jQuery.quoteString('apple')
174 | * "apple"
175 | *
176 | * >>> jQuery.quoteString('"Where are we going?", she asked.')
177 | * "\"Where are we going?\", she asked."
178 | */
179 | $.quoteString = function( string ) {
180 | if ( string.match( escapeable ) ) {
181 | return '"' + string.replace( escapeable, function( a ) {
182 | var c = meta[a];
183 | if ( typeof c === 'string' ) {
184 | return c;
185 | }
186 | c = a.charCodeAt();
187 | return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
188 | }) + '"';
189 | }
190 | return '"' + string + '"';
191 | };
192 |
193 | })( jQuery );
194 |
--------------------------------------------------------------------------------
/server/oauth/src/main/java/com/springsource/oauthservice/develop/JdbcAppRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.oauthservice.develop;
17 |
18 | import java.sql.ResultSet;
19 | import java.sql.SQLException;
20 | import java.util.List;
21 |
22 | import javax.inject.Inject;
23 |
24 | import org.springframework.jdbc.core.JdbcTemplate;
25 | import org.springframework.jdbc.core.RowMapper;
26 | import org.springframework.security.crypto.encrypt.TextEncryptor;
27 | import org.springframework.security.crypto.keygen.KeyGenerators;
28 | import org.springframework.security.crypto.keygen.StringKeyGenerator;
29 | import org.springframework.stereotype.Repository;
30 | import org.springframework.transaction.annotation.Transactional;
31 |
32 | import com.springsource.oauthservice.utils.SlugUtils;
33 |
34 | @Repository
35 | public class JdbcAppRepository implements AppRepository {
36 |
37 | private JdbcTemplate jdbcTemplate;
38 |
39 | private TextEncryptor encryptor;
40 |
41 | private StringKeyGenerator keyGenerator = KeyGenerators.string();
42 |
43 | @Inject
44 | public JdbcAppRepository(JdbcTemplate jdbcTemplate, TextEncryptor encryptor) {
45 | this.jdbcTemplate = jdbcTemplate;
46 | this.encryptor = encryptor;
47 | }
48 |
49 | public List findAppSummaries(String developerId) {
50 | return jdbcTemplate.query(SELECT_APPS, appSummaryMapper, developerId);
51 | }
52 |
53 | public App findAppBySlug(String developerId, String slug) {
54 | return jdbcTemplate.queryForObject(SELECT_APP_BY_SLUG, appMapper, developerId, slug);
55 | }
56 |
57 | public String updateApp(String developerId, String slug, AppForm form) {
58 | String newSlug = createSlug(form.getName());
59 | jdbcTemplate.update(UPDATE_APP_FORM, form.getName(), newSlug, form.getDescription(), form.getOrganization(), form.getWebsite(), form.getCallbackUrl(), developerId, slug);
60 | return newSlug;
61 | }
62 |
63 | public void deleteApp(String developerId, String slug) {
64 | jdbcTemplate.update(DELETE_APP, developerId, slug);
65 | }
66 |
67 | public AppForm getNewAppForm() {
68 | return new AppForm();
69 | }
70 |
71 | public AppForm getAppForm(String developerId, String slug) {
72 | return jdbcTemplate.queryForObject(SELECT_APP_FORM, appFormMapper, developerId, slug);
73 | }
74 |
75 | @Transactional
76 | public String createApp(String developerId, AppForm form) {
77 | String slug = createSlug(form.getName());
78 | String encryptedApiKey = encryptor.encrypt(keyGenerator.generateKey());
79 | String encryptedSecret = encryptor.encrypt(keyGenerator.generateKey());
80 | jdbcTemplate.update(INSERT_APP, form.getName(), slug, form.getDescription(), form.getOrganization(), form.getWebsite(), encryptedApiKey, encryptedSecret, form.getCallbackUrl());
81 | Long appId = jdbcTemplate.queryForLong("select lastVal()");
82 | jdbcTemplate.update(INSERT_APP_DEVELOPER, appId, developerId);
83 | return slug;
84 | }
85 |
86 | private String createSlug(String appName) {
87 | return SlugUtils.toSlug(appName);
88 | }
89 |
90 | private static final String SELECT_APPS = "select a.name, a.slug, a.description from App a inner join AppDeveloper d on a.id = d.app where d.developer = ?";
91 |
92 | private static final String SELECT_APP_BY_SLUG = "select a.name, a.slug, a.description, a.apiKey, a.secret, a.redirectUrl from App a inner join AppDeveloper d on a.id = d.app where d.developer = ? and a.slug = ?";
93 |
94 | private static final String SELECT_APP_FORM = "select a.name, a.description, a.organization, a.website, a.redirectUrl from App a inner join AppDeveloper d on a.id = d.app where d.developer = ? and a.slug = ?";
95 |
96 | private static final String UPDATE_APP_FORM = "update App set name = ?, slug = ?, description = ?, organization = ?, website = ?, redirectUrl = ? where exists(select 1 from AppDeveloper where developer = ?) and slug = ?";
97 |
98 | private static final String DELETE_APP = "delete from App where exists(select 1 from AppDeveloper where developer = ?) and slug = ?";
99 |
100 | private static final String INSERT_APP = "insert into App (name, slug, description, organization, website, apiKey, secret, redirectUrl, grantTypes) values (?, ?, ?, ?, ?, ?, ?, ?, 'password,authorization_code,refresh_token')";
101 |
102 | private static final String INSERT_APP_DEVELOPER = "insert into AppDeveloper (app, developer) values (?, ?)";
103 |
104 | private RowMapper appMapper = new RowMapper() {
105 | public App mapRow(ResultSet rs, int rowNum) throws SQLException {
106 | return new App(appSummaryMapper.mapRow(rs, rowNum), encryptor.decrypt(rs.getString("apiKey")), encryptor.decrypt(rs.getString("secret")), rs.getString("redirectUrl"));
107 | }
108 | };
109 |
110 | private RowMapper appSummaryMapper = new RowMapper() {
111 | public AppSummary mapRow(ResultSet rs, int rowNum) throws SQLException {
112 | // TODO this is currently hardcoded
113 | String iconUrl = "https://images.greenhouse.springsource.org/apps/icon-default-app.png";
114 | return new AppSummary(rs.getString("name"), iconUrl, rs.getString("description"), rs.getString("slug"));
115 | }
116 | };
117 |
118 | private RowMapper appFormMapper = new RowMapper() {
119 | public AppForm mapRow(ResultSet rs, int rowNum) throws SQLException {
120 | AppForm form = new AppForm();
121 | form.setName(rs.getString("name"));
122 | form.setDescription(rs.getString("description"));
123 | form.setOrganization(rs.getString("organization"));
124 | form.setWebsite(rs.getString("website"));
125 | form.setCallbackUrl(rs.getString("redirectUrl"));
126 | return form;
127 | }
128 | };
129 | }
130 |
--------------------------------------------------------------------------------
/server/api/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ##############################################################################
4 | ## ##
5 | ## Gradle wrapper script for UN*X ##
6 | ## ##
7 | ##############################################################################
8 |
9 | # Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
10 | # GRADLE_OPTS="$GRADLE_OPTS -Xmx512m"
11 | # JAVA_OPTS="$JAVA_OPTS -Xmx512m"
12 |
13 | GRADLE_APP_NAME=Gradle
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set JAVA_HOME if it's not already set.
46 | if [ -z "$JAVA_HOME" ] ; then
47 | if $darwin ; then
48 | [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home"
49 | [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
50 | else
51 | javaExecutable="`which javac`"
52 | [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME."
53 | # readlink(1) is not available as standard on Solaris 10.
54 | readLink=`which readlink`
55 | [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME."
56 | javaExecutable="`readlink -f \"$javaExecutable\"`"
57 | javaHome="`dirname \"$javaExecutable\"`"
58 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
59 | export JAVA_HOME="$javaHome"
60 | fi
61 | fi
62 |
63 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
64 | if $cygwin ; then
65 | [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"`
66 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
67 | fi
68 |
69 | STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain
70 | CLASSPATH=`dirname "$0"`/gradle/wrapper/gradle-wrapper.jar
71 | WRAPPER_PROPERTIES=`dirname "$0"`/gradle/wrapper/gradle-wrapper.properties
72 | # Determine the Java command to use to start the JVM.
73 | if [ -z "$JAVACMD" ] ; then
74 | if [ -n "$JAVA_HOME" ] ; then
75 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
76 | # IBM's JDK on AIX uses strange locations for the executables
77 | JAVACMD="$JAVA_HOME/jre/sh/java"
78 | else
79 | JAVACMD="$JAVA_HOME/bin/java"
80 | fi
81 | else
82 | JAVACMD="java"
83 | fi
84 | fi
85 | if [ ! -x "$JAVACMD" ] ; then
86 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 | if [ -z "$JAVA_HOME" ] ; then
92 | warn "JAVA_HOME environment variable is not set"
93 | fi
94 |
95 | # Increase the maximum file descriptors if we can.
96 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
97 | MAX_FD_LIMIT=`ulimit -H -n`
98 | if [ $? -eq 0 ] ; then
99 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
100 | MAX_FD="$MAX_FD_LIMIT"
101 | fi
102 | ulimit -n $MAX_FD
103 | if [ $? -ne 0 ] ; then
104 | warn "Could not set maximum file descriptor limit: $MAX_FD"
105 | fi
106 | else
107 | warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
108 | fi
109 | fi
110 |
111 | # For Darwin, add GRADLE_APP_NAME to the JAVA_OPTS as -Xdock:name
112 | if $darwin; then
113 | JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GRADLE_APP_NAME"
114 | # we may also want to set -Xdock:image
115 | fi
116 |
117 | # For Cygwin, switch paths to Windows format before running java
118 | if $cygwin ; then
119 | JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"`
120 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
121 |
122 | # We build the pattern for arguments to be converted via cygpath
123 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
124 | SEP=""
125 | for dir in $ROOTDIRSRAW ; do
126 | ROOTDIRS="$ROOTDIRS$SEP$dir"
127 | SEP="|"
128 | done
129 | OURCYGPATTERN="(^($ROOTDIRS))"
130 | # Add a user-defined pattern to the cygpath arguments
131 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
132 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
133 | fi
134 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
135 | i=0
136 | for arg in "$@" ; do
137 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
138 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
139 |
140 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
141 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
142 | else
143 | eval `echo args$i`="\"$arg\""
144 | fi
145 | i=$((i+1))
146 | done
147 | case $i in
148 | (0) set -- ;;
149 | (1) set -- "$args0" ;;
150 | (2) set -- "$args0" "$args1" ;;
151 | (3) set -- "$args0" "$args1" "$args2" ;;
152 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
153 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
154 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
155 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
156 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
157 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
158 | esac
159 | fi
160 |
161 | GRADLE_APP_BASE_NAME=`basename "$0"`
162 |
163 | exec "$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \
164 | -classpath "$CLASSPATH" \
165 | -Dorg.gradle.appname="$GRADLE_APP_BASE_NAME" \
166 | -Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \
167 | $STARTER_MAIN_CLASS \
168 | "$@"
169 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/config/BatchConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.config;
17 |
18 | import com.springsource.html5expense.integrations.EligibleChargeProcessor;
19 | import com.springsource.html5expense.integrations.EligibleChargeProcessorHeaders;
20 | import org.springframework.batch.core.Job;
21 | import org.springframework.batch.core.JobParameters;
22 | import org.springframework.batch.core.JobParametersBuilder;
23 | import org.springframework.batch.core.launch.JobLauncher;
24 | import org.springframework.batch.core.launch.support.SimpleJobLauncher;
25 | import org.springframework.batch.core.repository.JobRepository;
26 | import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
27 | import org.springframework.batch.item.ItemWriter;
28 | import org.springframework.batch.item.file.FlatFileItemReader;
29 | import org.springframework.batch.item.file.mapping.DefaultLineMapper;
30 | import org.springframework.batch.item.file.mapping.PassThroughFieldSetMapper;
31 | import org.springframework.batch.item.file.transform.DefaultFieldSet;
32 | import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
33 | import org.springframework.batch.item.file.transform.FieldSet;
34 | import org.springframework.beans.factory.annotation.Autowired;
35 | import org.springframework.beans.factory.annotation.Qualifier;
36 | import org.springframework.beans.factory.annotation.Value;
37 | import org.springframework.context.annotation.*;
38 | import org.springframework.core.io.FileSystemResource;
39 | import org.springframework.integration.Message;
40 | import org.springframework.integration.MessageChannel;
41 | import org.springframework.integration.support.MessageBuilder;
42 | import org.springframework.jdbc.datasource.DataSourceTransactionManager;
43 |
44 | import javax.inject.Inject;
45 | import java.io.File;
46 | import java.math.BigDecimal;
47 | import java.util.Date;
48 | import java.util.List;
49 |
50 | /**
51 | * @author Josh Long
52 | */
53 | @Configuration
54 | @Import({DataSourceConfig.class, ComponentConfig.class})
55 | @ImportResource("/ec-loader.xml")
56 | public class BatchConfig {
57 |
58 | @Inject
59 | private DataSourceConfig dataSourceConfig;
60 |
61 | @Inject
62 | private ComponentConfig componentConfig;
63 |
64 | @Autowired
65 | @Qualifier("newEligibleCharges")
66 | private MessageChannel channel;
67 |
68 | private File batchFileDirectory;
69 |
70 | @Autowired
71 | public void setBatchFileDirectory(@Value("#{ systemProperties['user.home'] }") String userHome) throws Exception {
72 | batchFileDirectory = new File(userHome, "in");
73 | if (!batchFileDirectory.exists())
74 | batchFileDirectory.mkdirs();
75 |
76 | }
77 |
78 | @Bean
79 | public SimpleJobLauncher jobLauncher() throws Exception {
80 | SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
81 | jobLauncher.setJobRepository((JobRepository) this.jobRepository().getObject());
82 | return jobLauncher;
83 | }
84 |
85 | @Bean
86 | public EligibleChargeProcessor eligibleChargeProcessor() {
87 | return new EligibleChargeProcessor();
88 | }
89 |
90 | @Bean
91 | @Scope("step")
92 | public FlatFileItemReader reader(@Value("#{jobParameters[file]}") String resource) {
93 |
94 | File f = new File(this.batchFileDirectory, resource + ".csv");
95 |
96 | DelimitedLineTokenizer del = new DelimitedLineTokenizer();
97 | del.setNames("date,amount,category,merchant".split(","));
98 | del.setDelimiter(DelimitedLineTokenizer.DELIMITER_COMMA);
99 |
100 | DefaultLineMapper defaultLineMapper = new DefaultLineMapper();
101 | defaultLineMapper.setLineTokenizer(del);
102 | defaultLineMapper.setFieldSetMapper(new PassThroughFieldSetMapper());
103 | defaultLineMapper.afterPropertiesSet();
104 |
105 | FlatFileItemReader fileItemReader = new FlatFileItemReader();
106 | fileItemReader.setLineMapper(defaultLineMapper);
107 | fileItemReader.setResource(new FileSystemResource(f));
108 |
109 | return fileItemReader;
110 | }
111 |
112 | @Bean
113 | public ItemWriter writer() {
114 | return new MessageSendingItemWriter(this.channel);
115 | }
116 |
117 | @Bean
118 | public JobRepositoryFactoryBean jobRepository() throws Exception {
119 | JobRepositoryFactoryBean bean = new JobRepositoryFactoryBean();
120 | bean.setTransactionManager(new DataSourceTransactionManager(dataSourceConfig.dataSource()));
121 | bean.setDataSource(dataSourceConfig.dataSource());
122 | return bean;
123 | }
124 |
125 | public static class MessageSendingItemWriter implements ItemWriter {
126 |
127 | private MessageChannel channel;
128 |
129 | public MessageSendingItemWriter(MessageChannel channel) {
130 | this.channel = channel;
131 | }
132 |
133 | @Override
134 | public void write(List extends DefaultFieldSet> defaultFieldSets) throws Exception {
135 | for (DefaultFieldSet defaultFieldSet : defaultFieldSets) {
136 | Date date = defaultFieldSet.readDate(0);
137 | BigDecimal bigDecimal = defaultFieldSet.readBigDecimal(1);
138 | String category = defaultFieldSet.readString(2);
139 | String merchant = defaultFieldSet.readString(3);
140 |
141 | Message msg = MessageBuilder.withPayload(category)
142 | .setHeader(EligibleChargeProcessorHeaders.EC_AMOUNT, bigDecimal)
143 | .setHeader(EligibleChargeProcessorHeaders.EC_CATEGORY, category)
144 | .setHeader(EligibleChargeProcessorHeaders.EC_MERCHANT, merchant)
145 | .setHeader(EligibleChargeProcessorHeaders.EC_DATE, date)
146 | .build();
147 | this.channel.send(msg);
148 |
149 | }
150 | }
151 | }
152 |
153 |
154 | public static void main(String args[]) throws Exception {
155 |
156 | AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BatchConfig.class);
157 |
158 | Job job = annotationConfigApplicationContext.getBean("read-eligible-charges", Job.class);
159 |
160 | JobParametersBuilder builder = new JobParametersBuilder();
161 | builder.addString("file", "foo");
162 | builder.addLong("uid", System.currentTimeMillis());
163 | JobParameters jobParameters = builder.toJobParameters();
164 |
165 | JobLauncher jobLauncher = annotationConfigApplicationContext.getBean(JobLauncher.class);
166 | jobLauncher.run(job, jobParameters);
167 |
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/server/api/src/main/java/com/springsource/html5expense/controllers/ExpenseReportingApiController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.springsource.html5expense.controllers;
17 |
18 | import com.springsource.html5expense.EligibleCharge;
19 | import com.springsource.html5expense.Expense;
20 | import com.springsource.html5expense.ExpenseReport;
21 | import com.springsource.html5expense.ExpenseReportingService;
22 | import org.apache.commons.io.IOUtils;
23 | import org.apache.commons.logging.Log;
24 | import org.apache.commons.logging.LogFactory;
25 | import org.springframework.http.HttpStatus;
26 | import org.springframework.stereotype.Controller;
27 | import org.springframework.web.bind.annotation.*;
28 | import org.springframework.web.multipart.MultipartFile;
29 |
30 | import javax.inject.Inject;
31 | import javax.servlet.http.HttpServletRequest;
32 | import javax.servlet.http.HttpServletResponse;
33 | import java.io.InputStream;
34 | import java.io.OutputStream;
35 | import java.util.Arrays;
36 | import java.util.Collection;
37 | import java.util.List;
38 |
39 | /**
40 | * @author Roy Clarkson
41 | * @author Josh Long
42 | */
43 | @Controller
44 | @RequestMapping("/reports")
45 | public class ExpenseReportingApiController {
46 |
47 | private Log log = LogFactory.getLog(getClass());
48 |
49 | @Inject
50 | private ExpenseReportingService service;
51 |
52 | @ResponseStatus(HttpStatus.OK)
53 | @RequestMapping(method = RequestMethod.DELETE, value = "/expenses/{expenseId}")
54 | public void restoreExpenseToEligibleCharge(@PathVariable("expenseId") Integer expenseId) {
55 | Expense ex = service.getExpense(expenseId);
56 | service.restoreEligibleCharges(Arrays.asList(ex.getId()));
57 | }
58 |
59 |
60 | @RequestMapping(method = RequestMethod.GET, value = "/{reportId}/expenses", produces = "application/json")
61 | @ResponseBody
62 | public Collection expenseForExpenseReport(HttpServletRequest request, @PathVariable("reportId") Long reportId) {
63 | return this.service.getExpensesForExpenseReport(reportId);
64 | }
65 |
66 | /**
67 | * Create a new {@link com.springsource.html5expense.ExpenseReport} with an associated description for the purpose
68 | *
69 | * @param purpose the reason for the expense report. i.e. conference, business meal, etc.
70 | * @return the ID of the new expense report
71 | */
72 | @RequestMapping(method = RequestMethod.POST)
73 | @ResponseBody
74 | public Long createReport(@RequestParam(required = true) String purpose) {
75 | return service.createReport(purpose);
76 | }
77 |
78 | /**
79 | * Retrieve a list of charges that can be associated with an {@link com.springsource.html5expense.ExpenseReport}.
80 | * These charges are not currently associated with any other expense report.
81 | *
82 | * @return collection of {@link com.springsource.html5expense.EligibleCharge} objects
83 | */
84 | @RequestMapping(value = "/eligible-charges", method = RequestMethod.GET, produces = "application/json")
85 | @ResponseBody
86 | public Collection getEligibleCharges() {
87 | return service.getEligibleCharges();
88 | }
89 |
90 | private String buildMimeTypeForExpense(Expense e) {
91 | String ext = e.getReceiptExtension();
92 | String mime = null;
93 | if (ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg")) {
94 | mime = "image/jpeg";
95 | } else if (ext.equalsIgnoreCase("gif"))
96 | mime = "image/gif";
97 | else
98 | mime = "application/binary";
99 | return mime;
100 | }
101 |
102 |
103 | @RequestMapping(value = "/receipts/{expenseId}")
104 | public void renderMedia(HttpServletResponse httpServletResponse, OutputStream os, @PathVariable("expenseId") Integer expenseId) {
105 |
106 | Expense expense = service.getExpense(expenseId);
107 | httpServletResponse.setContentType(buildMimeTypeForExpense(expense));
108 | InputStream is = service.retrieveReceipt(expenseId);
109 | try {
110 | IOUtils.copyLarge(is, os);
111 | } catch (Exception e1) {
112 | log.error(e1);
113 | } finally {
114 | IOUtils.closeQuietly(is);
115 | IOUtils.closeQuietly(os);
116 | }
117 |
118 | /*
119 | try {
120 | is = new FileInputStream(f);
121 | httpServletResponse.setContentLength((int) f.length());
122 | IOUtils.copyLarge(is, os);
123 | } catch (Exception e1) {
124 | log.error(e1);
125 | } finally {
126 | if (is != null)
127 | IOUtils.closeQuietly(is);
128 | if (os != null)
129 | IOUtils.closeQuietly(os);
130 | }*/
131 | }
132 |
133 |
134 | @RequestMapping(value = "/{reportId}/expenses", method = RequestMethod.POST, produces = "application/json")
135 | @ResponseBody
136 | public Collection createExpenses(@PathVariable Long reportId, @RequestParam(required = true, value = "chargeId") Long chargeId) {
137 | return service.createExpenses(reportId, Arrays.asList(chargeId));
138 | }
139 |
140 | private String findExtensionFromFileName(String fn) {
141 | int lPosOfPeriod = fn.lastIndexOf(".");
142 | if (lPosOfPeriod != -1 && !fn.endsWith(".")) {
143 | return fn.substring(lPosOfPeriod + 1);
144 | }
145 | return null;
146 | }
147 |
148 | @RequestMapping(value = "/receipts", method = RequestMethod.POST)
149 | @ResponseBody
150 | public String attachReceipt(@RequestParam("reportId") Long reportId, @RequestParam("expenseId") Integer expenseId, @RequestParam("file") MultipartFile file) {
151 | try {
152 | byte[] bytesForImage = file.getBytes();
153 | String ext = findExtensionFromFileName(file.getOriginalFilename());
154 | if (ext != null) {
155 | ext = ext.trim().toLowerCase();
156 | }
157 | return service.attachReceipt(reportId, expenseId, ext, bytesForImage);
158 | } catch (Throwable th) {
159 | if (log.isErrorEnabled()) {
160 | log.error("Something went wrong trying to write the file out.", th);
161 | }
162 | }
163 | return null;
164 | }
165 |
166 |
167 | /**
168 | * Finalizes and submits the {@link com.springsource.html5expense.ExpenseReport} for review
169 | *
170 | * @param reportId the ID of the {@link com.springsource.html5expense.ExpenseReport}
171 | */
172 | @RequestMapping(value = "/{reportId}", method = RequestMethod.POST)
173 | @ResponseStatus(value = HttpStatus.OK)
174 | public void submitReport(@PathVariable("reportId") Long reportId) {
175 | service.submitReport(reportId);
176 | }
177 |
178 | @RequestMapping(value = "/{reportId}", method = RequestMethod.GET)
179 | @ResponseBody
180 | public ExpenseReport getReport(@PathVariable("reportId") Long reportId) {
181 | return service.getExpenseReport(reportId);
182 | }
183 |
184 | @RequestMapping(value = "/{reportId}", method = RequestMethod.DELETE)
185 | @ResponseStatus(HttpStatus.OK)
186 | public void deleteReport(@PathVariable("reportId") Long reportId) {
187 | this.service.deleteExpenseReport(reportId);
188 | }
189 |
190 | /**
191 | * Retrieves all of the open, or incomplete, expense reports for the user
192 | *
193 | * @return list of {@link com.springsource.html5expense.ExpenseReport} objects
194 | */
195 | @RequestMapping(method = RequestMethod.GET, produces = "application/json")
196 | @ResponseBody
197 | public List getOpenReports() {
198 | return service.getOpenReports();
199 | }
200 |
201 | /**
202 | * Retrieves all of the submitted expense reports for the user
203 | *
204 | * @return list of {@link ExpenseReport} objects
205 | */
206 | @RequestMapping(value = "/submitted", method = RequestMethod.GET, produces = "application/json")
207 | public
208 | @ResponseBody
209 | List getSubmittedReports() {
210 | return service.getSubmittedReports();
211 | }
212 |
213 | @RequestMapping(method = RequestMethod.POST, value = "/{reportId}/purpose")
214 | @ResponseStatus(value = HttpStatus.OK)
215 | public void updateReportPurpose(@PathVariable("reportId") Long reportId, String title) {
216 | service.updateExpenseReportPurpose(reportId, title);
217 | }
218 |
219 | @RequestMapping(method = RequestMethod.GET, value = "/open-reports")
220 | @ResponseBody
221 | public Collection openReports() {
222 | return service.getOpenReports();
223 | }
224 |
225 | }
226 |
--------------------------------------------------------------------------------
/server/api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | 4.0.0
6 |
7 | org.springsource
8 | html5expenses
9 | HTML5 Expenses
10 |
11 | war
12 |
13 | 1.0.0
14 |
15 |
16 | 2.1.6.RELEASE
17 | 1.6
18 | 3.1.0.RELEASE
19 | 0.8.1
20 | 2.1.0.RELEASE
21 | 1.0.0.M5
22 |
23 |
24 |
25 |
26 |
27 | org.mortbay.jetty
28 | servlet-api
29 | 3.0.20100224
30 |
31 |
32 | org.springframework.data
33 | spring-data-mongodb
34 | 1.0.0.RC1
35 |
36 |
37 | postgresql
38 | postgresql
39 | 8.3-603.jdbc3
40 |
41 |
42 | org.thymeleaf
43 | thymeleaf-spring3
44 | 1.1.4
45 |
46 |
47 | org.cloudfoundry
48 | cloudfoundry-runtime
49 | ${cloudfoundry.version}
50 |
51 |
52 | org.springframework
53 | spring-jdbc
54 | 3.1.0.RELEASE
55 |
56 |
57 | org.springframework
58 | spring-expression
59 | 3.1.0.RELEASE
60 |
61 |
62 | org.springframework
63 | spring-beans
64 | 3.1.0.RELEASE
65 |
66 |
67 | org.springframework
68 | spring-jms
69 | 3.1.0.RELEASE
70 |
71 |
72 |
73 |
74 |
75 |
76 | org.springframework.data
77 | spring-data-mongodb
78 |
79 |
80 | postgresql
81 | postgresql
82 |
83 |
84 | org.cloudfoundry
85 | cloudfoundry-runtime
86 |
87 |
88 | org.thymeleaf
89 | thymeleaf-spring3
90 |
91 |
92 |
93 | log4j
94 | log4j
95 | 1.2.9
96 |
97 |
98 |
99 | org.hibernate
100 | hibernate-entitymanager
101 | 3.6.5.Final
102 |
103 |
104 | commons-fileupload
105 | commons-fileupload
106 | 1.2.1
107 |
108 |
109 | commons-lang
110 | commons-lang
111 | 2.5
112 |
113 |
114 | com.h2database
115 | h2
116 | 1.2.144
117 |
118 |
119 | cglib
120 | cglib-nodep
121 | 2.2
122 |
123 |
124 | org.springframework
125 | spring-context
126 | ${org.springframework-version}
127 |
128 |
129 | commons-io
130 | commons-io
131 | 1.3
132 |
133 |
134 | org.springframework
135 | spring-webmvc
136 | ${org.springframework-version}
137 |
138 |
139 | org.springframework
140 | spring-orm
141 | ${org.springframework-version}
142 |
143 |
144 | javax.inject
145 | javax.inject
146 | 1
147 |
148 |
149 | org.codehaus.jackson
150 | jackson-mapper-asl
151 | 1.8.5
152 |
153 |
154 |
155 | org.springframework.batch
156 | spring-batch-core
157 | ${spring.batch.version}
158 |
159 |
160 |
161 | org.springframework.integration
162 | spring-integration-jms
163 | ${spring-integration.version}
164 |
165 |
166 | org.springframework
167 | spring-test
168 | ${org.springframework-version}
169 |
170 |
171 | org.springframework.security.oauth
172 | spring-security-oauth2
173 | ${spring-security-oauth2.version}
174 |
175 |
176 | junit
177 | junit
178 | 4.7
179 | test
180 |
181 |
182 | org.mortbay.jetty
183 | servlet-api
184 | provided
185 |
186 |
187 |
188 |
189 |
190 | org.apache.maven.plugins
191 | maven-compiler-plugin
192 | 2.3.2
193 |
194 | ${java-version}
195 | ${java-version}
196 |
197 |
198 |
199 | org.apache.maven.plugins
200 | maven-war-plugin
201 | 2.1.1
202 |
203 | false
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 | milestone
212 | https://maven.springframework.org/milestone
213 |
214 |
215 | release
216 | https://maven.springframework.org/release
217 |
218 |
219 | snapshot
220 | https://maven.springframework.org/snapshot
221 |
222 |
223 | sonatype-nexus-snapshots
224 | Sonatype Nexus Snapshots
225 | https://oss.sonatype.org/content/repositories/snapshots
226 |
227 | true
228 |
229 |
230 |
231 |
--------------------------------------------------------------------------------