Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add external user support realtime #82

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion backend/packages.pip
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Flask-Sockets==0.2.1
Flask-SQLAlchemy==2.1
Flask-Migrate==2.0.3
pyjwt==1.4.2
psycopg2==2.7.4
psycopg2==2.9.3
python-dateutil==2.6.0
python-dotenv==0.6.3
requests==2.24.0
Expand Down
6 changes: 4 additions & 2 deletions backend/src/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ class Entry(Base, db.Model):
backref=db.backref('entries', lazy='dynamic', cascade='all, delete-orphan'))

def parse(self, data, currentUser, reqType):
if currentUser:
is_new = self.id is None

if is_new and currentUser:
data['user'] = currentUser.id

if reqType == 'save' and currentUser and currentUser.has_running_entries():
Expand Down Expand Up @@ -161,4 +163,4 @@ class Ticket(Base, db.Model):

# Errors will occur if the name of the ticket is longer than the maximum length
# So better safe than sorry
name = db.Column(db.String(500))
name = db.Column(db.String(500))
6 changes: 3 additions & 3 deletions cy/pkg
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ version: FORCE
build: FORCE build-be build-fe

build-fe: FORCE
cd frontend && yarn --no-progress
cd frontend && yarn --frozen-lockfile
cd frontend && yarn run build
mv frontend/build static

build-be: FORCE
virtualenv --python=python3 venv
. venv/bin/activate && pip install -U pip==9.0.1
. venv/bin/activate && pip install -U pip==21.3.1
mkdir backend/packages/
. venv/bin/activate && cd backend/packages/ && pip download -r ../packages.pip
. venv/bin/activate && pip install -U backend/packages/*
Expand All @@ -43,7 +43,7 @@ pre-install: FORCE

install: FORCE
# Sigh. Need download to install pip that can do offline installs :(
pip install -U pip==9.0.1
pip install -U pip==21.3.1
pip install -U backend/packages/*
cd backend && ./manage.py db upgrade

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/component/Entry/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ export default class EntryProject extends Component {
};

render() {
const { entry, allowEdit } = this.props;
const { entry, allowEdit, viewStore } = this.props;
const project = entry.project
? this.props.projectStore.get(entry.project)
: null;

let projectOptions = [];
for(let index = 0; index < this.props.projectStore.models.length; index++){
const model = this.props.projectStore.models[index];
if(model && model.isActive){
if(model && model.isActive && viewStore.currentUser.isAllowedProject(model)){
projectOptions.push(formatProjectToOption(model));
}
}
Expand Down
16 changes: 10 additions & 6 deletions frontend/src/container/AppHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,16 @@ export default class Header extends Component {
<TopMenuLink activeClassName="selected" exact to="/">
My time
</TopMenuLink>
<TopMenuLink activeClassName="selected" to="/users">
Employees
</TopMenuLink>
<TopMenuLink activeClassName="selected" to="/projects">
Projects
</TopMenuLink>
{!this.props.store.currentUser.isExternal && (
<div style={{ display: 'inherit' }}>
<TopMenuLink activeClassName="selected" to="/users">
Employees
</TopMenuLink>
<TopMenuLink activeClassName="selected" to="/projects">
Projects
</TopMenuLink>
</div>
)}
<TopMenuLink activeClassName="selected" to="/archive">
Archive
</TopMenuLink>
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/container/EntryOverviewItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Icon from 'component/Icon';
import { ProjectStore } from 'store/Project';
import { UserStore } from 'store/User';
import { Entry } from 'store/Entry';
import View from 'store/View';
import IconDelete from 'image/icon-delete.svg';
import IconCopy from 'image/icon-copy.svg';
import IconTicketLink from 'image/icon-ticket-link.svg';
Expand All @@ -29,6 +30,7 @@ export default class EntryOverviewItem extends Component {
onCopy: PropTypes.func.isRequired,
projectStore: PropTypes.instanceOf(ProjectStore).isRequired,
userStore: PropTypes.instanceOf(UserStore),
viewStore: PropTypes.instanceOf(View).isRequired,
allowEdit: PropTypes.bool,
};

Expand All @@ -48,7 +50,7 @@ export default class EntryOverviewItem extends Component {
};

render() {
const { entry, allowEdit, projectStore } = this.props;
const { entry, allowEdit, projectStore, viewStore } = this.props;
const diffMinutes = entry.differenceInMinutes;

const wbso = entry.wbso;
Expand All @@ -72,6 +74,7 @@ export default class EntryOverviewItem extends Component {
entry={entry}
projectStore={projectStore}
allowEdit={allowEdit}
viewStore={viewStore}
/>
<EntryItemActions>
<a
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/container/TimeEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ export default class TimeEntry extends Component {
}

render() {
const { entry } = this.props;
const { entry, viewStore } = this.props;

let projectOptions = [];
for(let index = 0; index < this.props.projectStore.models.length; index++){
const model = this.props.projectStore.models[index];
if(model && model.isActive){
if(model && model.isActive && viewStore.currentUser.isAllowedProject(model)){
projectOptions.push(this.formatProjectToOption(model));
}
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/screen/UserEntries.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ export default class UserEntriesScreen extends Component {
<div>
<Title>Employee {user ? user.displayName : 'Unknown'}</Title>
<EntryOverview
viewStore={this.props.viewStore}
entries={this.entryStore}
projectStore={this.projectStore}
allowEdit={this.props.viewStore.currentUser.pmc === 'Management'}
/>
</div>
);
Expand Down
28 changes: 27 additions & 1 deletion frontend/src/store/User.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { observable } from 'mobx';
import { observable, computed } from 'mobx';
import { Model, Store } from './Base';
import { trimStart } from 'lodash';

// Defines if a user is "external". The RealTime UI will hide certain menu
// items. Example:
//
// ext.Project X
//
// This way menu items are hidden, and only projects with name which matches
// the prefix "Project X" will be shown.
const EXTERNAL_PMC_PREFIX = 'ext.';

export class User extends Model {
target = 'user';
Expand All @@ -11,6 +21,22 @@ export class User extends Model {
@observable pmc = '';
@observable stillWorking = '';

@computed
get isExternal() {
// Super secure.
return this.pmc.startsWith(EXTERNAL_PMC_PREFIX);
}

isAllowedProject(project) {
if (this.isExternal) {
const allowedPrefix = trimStart(this.pmc, EXTERNAL_PMC_PREFIX);

return project.name.startsWith(allowedPrefix);
}

return true;
}

setToken(token) {
localStorage.setItem('jwt-auth-token', token);
}
Expand Down
Loading