Merge remote-tracking branch 'upstream/application-mgt' into appm_new

feature/appm-store/pbac
megala21 7 years ago
commit ddbaa0e6b0

@ -34,32 +34,6 @@
<build>
<plugins>
<plugin>
<groupId>org.wso2.maven</groupId>
<artifactId>carbon-p2-plugin</artifactId>
<version>${carbon.p2.plugin.version}</version>
<executions>
<execution>
<id>4-p2-feature-generation</id>
<phase>package</phase>
<goals>
<goal>p2-feature-gen</goal>
</goals>
<configuration>
<id>org.wso2.carbon.device.application.mgt.store</id>
<propertiesFile>../../etc/feature.properties</propertiesFile>
<adviceFile>
<properties>
<propertyDef>org.wso2.carbon.p2.category.type:server
</propertyDef>
<propertyDef>org.eclipse.equinox.p2.type.group:false
</propertyDef>
</properties>
</adviceFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
@ -86,7 +60,7 @@
</goals>
<phase>compile</phase>
<configuration>
<workingDirectory>${basedir}/src/main/resources/publisher</workingDirectory>
<workingDirectory>${basedir}/src/main/resources/store</workingDirectory>
<executable>${npm.executable}</executable>
<arguments>
<argument>run</argument>

@ -0,0 +1,49 @@
{
"name": "store",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.16.2",
"flux": "^3.1.3",
"history": "^4.6.3",
"latest-version": "^3.1.0",
"material-ui": "^0.19.0",
"prop-types": "^15.5.10",
"qs": "^6.5.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-dropzone": "^4.1.0",
"react-images-uploader": "^1.1.0",
"react-material-ui-form-validator": "^0.5.0",
"react-modal": "^2.2.2",
"react-router": "^4.1.2",
"react-router-dom": "^4.1.2",
"react-scripts": "1.0.10",
"react-sliding-pane": "^1.2.3",
"react-tap-event-plugin": "^2.0.1"
},
"devDependencies": {
"babel-core": "^6.24.1",
"babel-loader": "^7.1.2",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-register": "^6.24.1",
"chai": "^4.0.2",
"css-loader": "^0.28.7",
"less": "^2.7.2",
"less-loader": "^4.0.4",
"mocha": "^3.4.1",
"mock-local-storage": "^1.0.2",
"style-loader": "^0.18.1",
"webpack": "^2.7.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"build_prod": "NODE_ENV=production webpack -p --progress --colors --config webpack.config.js",
"build_dev": "NODE_ENV=development webpack -d --config webpack.config.js --watch "
}
}

@ -0,0 +1,40 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

@ -0,0 +1,113 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
import createHistory from 'history/createBrowserHistory';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import Login from './components/Login';
import BaseLayout from './components/BaseLayout';
import NotFound from './components/NotFound';
const history = createHistory({basename: '/store'});
/**
* User can define the themes in the config.json. The themes will be loaded based on the user preference.
*/
const theme = require("./config.json").theme;
//
let muiTheme = null;
if (theme.current === "default") {
let defaultTheme = require("material-ui/styles/baseThemes/" + theme.default);
muiTheme = getMuiTheme(defaultTheme.default);
}
/**
* This component defines the layout and the routes for the app.
* All the content will be loaded inside the Base component.
* The base component includes the Core layout and the routers according to which the content will be displayed.
*
* The Router and Route components.
* The Router and Route is used for navigation.
* We specify the component which needs to be rendered for an URL.
* Ex: When navigate to publisher/overview, the overview component will be rendered inside the main layout.
*
* HashRouter is used because the other router types need the server to serve those urls. In hashRouter, server does
* not want to serve the URL.
* */
class Base extends Component {
render() {
return (
<div className="container">
<BaseLayout state={this.props.state} updateState={this.props.updateState}>
<Switch>
<Route component={NotFound}/>
</Switch>
</BaseLayout>
</div>
)
}
}
Base.propTypes = {
updateState: React.PropTypes.func.isRequired
};
/**
* This component is referred by the index.js to initiate the application.
* TODO: Currently the URL shows like https://localhost:9443/publisher/#/publisher/assets/apps/create. this needs to
* be fixed as https://localhost:9443/publisher/#/assets/apps/create
*
* */
class Store extends Component {
constructor() {
super();
if (!this.state) {
this.state = {};
this.state.store = {};
}
this.updateState = this.updateState.bind(this);
}
render() {
return (
<div className="App">
<MuiThemeProvider muiTheme={muiTheme}>
<Router basename="store" history={history}>
<Switch>
<Route path="/login"
render={routeProps => <Login {...routeProps} updateState={this.updateState} state={this.state}/>}/>
<Route path="/logout"
render={routeProps => <Base {...routeProps} updateState={this.updateState} state={this.state}/>}/>
<Route
render={routeProps => <Base {...routeProps} updateState={this.updateState} state={this.state}/>}/>
</Switch>
</Router>
</MuiThemeProvider>
</div>
);
}
updateState(data) {
this.setState(data);
}
}
export default Store;

@ -0,0 +1,132 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import Badge from 'material-ui/Badge';
import React, {Component} from 'react';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import IconButton from 'material-ui/IconButton';
import {List, ListItem} from 'material-ui/List';
import Apps from 'material-ui/svg-icons/navigation/apps';
import NotificationsIcon from 'material-ui/svg-icons/social/notifications';
import ActionAccountCircle from 'material-ui/svg-icons/action/account-circle';
import {Link, withRouter} from 'react-router-dom';
/**
* Base Layout:
* App bar
* Left Navigation
* Middle content.
* */
class BaseLayout extends Component {
handleApplicationClick() {
this.handleHistory('/assets/apps');
}
/**
* The method to update the history.
* to: The URL to route.
* */
handleHistory(to) {
this.props.history.push(to);
}
handleUserLogin() {
if (this.props.state.store.user) {
return (
<IconButton tooltip={this.props.state.store.user}>
<ActionAccountCircle/>
</IconButton>
);
} else {
return (
<Link to='/login'> Login</Link>
);
}
}
handleNotification() {
if (this.props.state.store.user) {
return (
<Badge
badgeContent={this.props.state.store.notifications}
secondary={true}
badgeStyle={{top: 12, right: 12}}>
<IconButton tooltip="Notifications">
<NotificationsIcon/>
</IconButton>
</Badge>
);
}
}
render() {
return (
<div>
<AppBar title="App Store"
iconElementRight={
<div>
{this.handleNotification()}
{this.handleUserLogin()}
</div>
}
/>
<div>
<Drawer containerStyle={{height: 'calc(100% - 64px)', width: '15%', top: '13%', left: '1%'}}
open={true}>
<List>
<ListItem primaryText="Applications"
leftIcon={<Apps/>}
initiallyOpen={false}
primaryTogglesNestedList={true}
onClick={this.handleApplicationClick.bind(this)}
nestedItems={[
<ListItem
key={1}
primaryText="Business" //TODO: categoryies ...
leftIcon={<List/>}
/>]}
/>
</List>
</Drawer>
</div>
<div style=
{
{
height: 'calc(100% - 64px)',
marginLeft: '16%',
width: 'calc(100%-15%)',
top: 64,
left: "-100px"
}
}>
{this.props.children}
</div>
</div>);
}
}
BaseLayout.propTypes = {
children: PropTypes.element
};
export default withRouter(BaseLayout);

@ -0,0 +1,168 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import qs from 'qs';
import React, {Component} from 'react';
import Checkbox from 'material-ui/Checkbox';
import {Redirect, Route} from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import {Card, CardActions, CardTitle} from 'material-ui/Card';
import {TextValidator, ValidatorForm} from 'react-material-ui-form-validator';
import Store from '../App';
//todo: remove the {TextValidator, ValidatorForm} and implement it manually.
/**
* The Login Component.
*
* This component contains the Login form and methods to handle field change events.
* The user name and password will be set to the state and sent to the api.
*
* If the user is already logged in, it will redirect to the last point where the user was.
* */
class Login extends Component {
constructor() {
super();
this.state = {
isLoggedIn: false,
referrer: "/",
userName: "",
rememberMe: true
}
}
componentDidMount() {
let queryString = this.props.location.search;
console.log(queryString);
queryString = queryString.replace(/^\?/, '');
/* With QS version up we can directly use {ignoreQueryPrefix: true} option */
let params = qs.parse(queryString);
if (params.referrer) {
this.setState({referrer: params.referrer});
}
}
handleLogin(event) {
event.preventDefault();
console.log(this.props);
//TODO: send authentication request.
let location = {
pathname: this.state.referrer
};
let storeState = {
store : {
user: this.state.userName,
notifications: 0
}
};
this.props.updateState(storeState);
this.props.history.push(location);
}
/**
* Handles the username field change event.
* */
onUserNameChange(event) {
this.setState(
{
userName: event.target.value
}
);
}
/**
* Handles the password field change event.
* */
onPasswordChange(event) {
this.setState(
{
password: event.target.value
}
);
}
/**
* Handles the remember me check.
* */
handleRememberMe() {
this.setState(
{
rememberMe: !this.state.rememberMe
}
);
}
handleSuccessfulLogin() {
return (
<Redirect to='/store'/>
);
}
render() {
if (!(this.state.isLoggedIn && this.state.userName)) {
return (
<div>
{/*TODO: Style the components.*/}
<Card>
<CardTitle title="WSO2 IoT App Store"/>
<CardActions>
<ValidatorForm
ref="form"
onSubmit={this.handleLogin.bind(this)}
onError={errors => console.log(errors)}>
<TextValidator
floatingLabelText="User Name"
floatingLabelFixed={true}
onChange={this.onUserNameChange.bind(this)}
name="userName"
validators={['required']}
errorMessages={['User Name is required']}
value={this.state.userName}
/>
<br/>
<TextValidator
floatingLabelText="Password"
floatingLabelFixed={true}
onChange={this.onPasswordChange.bind(this)}
name="password"
type="password"
value={this.state.password}
validators={['required']}
errorMessages={['Password is required']}
/>
<br/>
<Checkbox label="Remember me."
onCheck={this.handleRememberMe.bind(this)}
checked={this.state.rememberMe}/>
<br/>
<RaisedButton type="submit" label="Login"/>
</ValidatorForm>
</CardActions>
</Card>
</div>);
} else {
this.handleSuccessfulLogin();
}
}
}
export default Login;

@ -0,0 +1,38 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
/**
* Error page.
* */
class Error extends Component {
constructor() {
super();
}
render() {
return (
<div>
404 not found
</div>
);
}
}
export default Error;

@ -16,24 +16,10 @@
* under the License.
*/
import BaseLayout from './BaseLayout';
/**
* This a sample custom theme file. In config.json, if the following changes are done, this theme will be applied.
* {
* "theme" : {
* "type" : "custom",
* "value" : "custom-theme"
* }
* }
* Contains all UI components related to Application, Login and Platform
*/
import {
indigo500, indigo700, redA200,
} from 'material-ui/styles/colors';
export default {
palette: {
primary1Color: indigo500,
primary2Color: indigo700,
accent1Color: redA200,
pickerHeaderColor: indigo500,
},
};
export default {BaseLayout};

@ -0,0 +1,7 @@
{
"theme" : {
"current" : "default",
"default" : "lightBaseTheme",
"custom" : "custom-theme"
}
}

@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Store from './App';
ReactDOM.render(<Store />, document.getElementById('root'));

@ -0,0 +1,72 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var webpack = require('webpack');
var path = require('path');
var BUILD_DIR = path.resolve(__dirname, 'public/dist');
var APP_DIR = path.resolve(__dirname, 'src');
const config = {
entry: {
index: APP_DIR+ '/index.js'
},
output: {
path: path.resolve(__dirname, BUILD_DIR),
filename: 'index.js'
},
resolve: {
extensions: ['*', '.jsx', '.js']
},
devtool: "source-map",
plugins: [],
watch: false,
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: APP_DIR,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015', 'react'],
plugins: ['transform-class-properties']
}
}
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}
]
}
};
module.exports = config;
Loading…
Cancel
Save