Commit 7255c33d authored by Florent Chehab's avatar Florent Chehab
Browse files

Render Generic Module partly connected

parent aa616a2a
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
from django.conf.urls import url, include from django.conf.urls import url, include
from django.conf import settings from django.conf import settings
from rest_framework import routers from rest_framework import routers
from django.urls import path
from . import views
ALL_MODELS = [] ALL_MODELS = []
ALL_VIEWSETS = [] ALL_VIEWSETS = []
...@@ -51,6 +53,7 @@ router.register( ...@@ -51,6 +53,7 @@ router.register(
){% endif %}{% endfor %} ){% endif %}{% endfor %}
urlpatterns += [url(r'^api/', include(router.urls))] urlpatterns += [url(r'^api/', include(router.urls))]
urlpatterns.append(path('api/serverModerationStatus/', views.app_moderation_status))
for model in ALL_MODELS: for model in ALL_MODELS:
for key in model.model_config: for key in model.model_config:
......
...@@ -4,13 +4,14 @@ from backend.models.university import University ...@@ -4,13 +4,14 @@ from backend.models.university import University
from backend.fields import JSONField from backend.fields import JSONField
from backend.models.abstract.my_model import MyModel, MyModelSerializer, MyModelViewSet from backend.models.abstract.my_model import MyModel, MyModelSerializer, MyModelViewSet
from django.contrib.auth.models import User from django.contrib.auth.models import User
from backend.utils import get_viewset_permissions, get_model_config from backend.utils import get_viewset_permissions, get_model_config, get_user_level
class UserData(MyModel): class UserData(MyModel):
model_config = get_model_config("UserData") model_config = get_model_config("UserData")
owner = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) owner = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)
contact_info = JSONField(default=dict) contact_info = JSONField(default=dict)
contact_info_is_public = models.BooleanField(default=False) contact_info_is_public = models.BooleanField(default=False)
config = JSONField(default=dict) config = JSONField(default=dict)
...@@ -21,6 +22,10 @@ class UserData(MyModel): ...@@ -21,6 +22,10 @@ class UserData(MyModel):
class UserDataSerializer(MyModelSerializer): class UserDataSerializer(MyModelSerializer):
owner = serializers.CharField(read_only=True) owner = serializers.CharField(read_only=True)
owner_level = serializers.SerializerMethodField()
def get_owner_level(self, obj):
return get_user_level(obj.owner)
def my_pre_save(self): def my_pre_save(self):
self.override_validated_data({'owner': self.user}) self.override_validated_data({'owner': self.user})
......
...@@ -3,6 +3,15 @@ from django.conf import settings ...@@ -3,6 +3,15 @@ from django.conf import settings
from .obj_moderation_permission import OBJ_MODERATION_PERMISSIONS from .obj_moderation_permission import OBJ_MODERATION_PERMISSIONS
######################################
######################################
##
# IF YOU TOUCH THIS FILE, MODIFY IT'S
# JS EQUIVALENT (isModerationRequired)
##
######################################
######################################
def is_moderation_required(model_moderation_level, obj_in_db, user, user_level=None): def is_moderation_required(model_moderation_level, obj_in_db, user, user_level=None):
if user_level is None: if user_level is None:
user_level = get_user_level(user) user_level = get_user_level(user)
......
...@@ -8,7 +8,8 @@ def get_model_config(model): ...@@ -8,7 +8,8 @@ def get_model_config(model):
if obj['model'] == model: if obj['model'] == model:
return { return {
"moderation_level": obj["moderation_level"], "moderation_level": obj["moderation_level"],
"model": model "model": model,
"read_only": obj["read_only"]
} }
raise Exception("Model not found in API configuraiton, cannot process !") raise Exception("Model not found in API configuraiton, cannot process !")
from django.conf import settings
from django.http import HttpResponse
import json
from backend.permissions.obj_moderation_permission import OBJ_MODERATION_PERMISSIONS
def app_moderation_status(request):
return HttpResponse(json.dumps({
'activated': settings.MODERATION_ACTIVATED,
'moderator_level': OBJ_MODERATION_PERMISSIONS["moderator"]
}))
...@@ -4,7 +4,7 @@ from os import makedirs ...@@ -4,7 +4,7 @@ from os import makedirs
from os.path import join, dirname, realpath, exists from os.path import join, dirname, realpath, exists
from django import template from django import template
import re import re
import yaml from general.api import get_api_config
############ ############
# Need to do this first so that Django template engine is working # Need to do this first so that Django template engine is working
...@@ -37,10 +37,8 @@ templates = [ ...@@ -37,10 +37,8 @@ templates = [
'combinedReducers' 'combinedReducers'
] ]
api_config = get_api_config()
API_BASE = "/api/" API_BASE = "/api/"
with open(join(current_dir, '../../general/api/api_config.yml'), 'r') as f:
data = f.read()
api_config = yaml.load(data)
contexts = [] contexts = []
for api in api_config: for api in api_config:
...@@ -53,6 +51,12 @@ for api in api_config: ...@@ -53,6 +51,12 @@ for api in api_config:
"api_end_point": API_BASE + api["api_end_point"] + '/', "api_end_point": API_BASE + api["api_end_point"] + '/',
}) })
# API outside rest framework here
contexts.append({
"name": 'serverModerationStatus',
"api_end_point": API_BASE + 'serverModerationStatus' + '/',
})
def convert(name): def convert(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import Loading from './other/Loading'; import Loading from './other/Loading';
import _ from 'underscore';
class MyComponent extends Component { class MyComponent extends Component {
customErrorHandlers = {} customErrorHandlers = {}
idToUse = null; idToUse = null;
...@@ -14,9 +14,6 @@ class MyComponent extends Component { ...@@ -14,9 +14,6 @@ class MyComponent extends Component {
const { props } = this; const { props } = this;
for (let prop_key in props) { for (let prop_key in props) {
let prop = props[prop_key]; let prop = props[prop_key];
// if (typeof prop == 'boolean') {
// continue;
// }
if (prop === Object(prop) && 'fetched' in prop) { if (prop === Object(prop) && 'fetched' in prop) {
out[prop_key] = prop.fetched.data; out[prop_key] = prop.fetched.data;
} }
...@@ -37,9 +34,6 @@ class MyComponent extends Component { ...@@ -37,9 +34,6 @@ class MyComponent extends Component {
const { props } = this; const { props } = this;
for (let el in props) { for (let el in props) {
let prop = props[el]; let prop = props[el];
// if (typeof prop == 'boolean') {
// continue;
// }
if (prop === Object(prop) && val in prop && prop[val]) { if (prop === Object(prop) && val in prop && prop[val]) {
return true; return true;
} }
...@@ -51,9 +45,6 @@ class MyComponent extends Component { ...@@ -51,9 +45,6 @@ class MyComponent extends Component {
const { props } = this; const { props } = this;
for (let prop_key in props) { for (let prop_key in props) {
let prop = props[prop_key]; let prop = props[prop_key];
if (typeof prop == 'boolean') {
continue;
}
if (prop === Object(prop) && 'fetchHasError' in prop) { if (prop === Object(prop) && 'fetchHasError' in prop) {
if (prop.fetchHasError.status) { if (prop.fetchHasError.status) {
if (prop_key in this.customErrorHandlers) { if (prop_key in this.customErrorHandlers) {
...@@ -80,18 +71,19 @@ class MyComponent extends Component { ...@@ -80,18 +71,19 @@ class MyComponent extends Component {
return this.checkProps('isSaving'); return this.checkProps('isSaving');
} }
loadPropsIfNeeded() { loadPropsIfNeeded(dontFetch = false) {
let propsLoaded = true;
if (this.checkPropsHasError()) { if (this.checkPropsHasError()) {
return false; return false;
} }
const { props } = this; const { props } = this;
for (let prop_key in props) { for (let prop_key in props) {
let prop = props[prop_key]; let prop = props[prop_key];
if (typeof prop == 'boolean') {
continue;
}
if (prop === Object(prop) && 'fetched' in prop) { if (prop === Object(prop) && 'fetched' in prop) {
if ((!prop.fetched.fetchedAt) || prop.invalidated) { if ((!prop.fetched.fetchedAt) || prop.invalidated) {
propsLoaded = false;
if (!dontFetch) {
if (!prop.isLoading) { if (!prop.isLoading) {
if (this.idToUse) { if (this.idToUse) {
props.fetchData[prop_key](this.props[this.idToUse]); props.fetchData[prop_key](this.props[this.idToUse]);
...@@ -103,13 +95,11 @@ class MyComponent extends Component { ...@@ -103,13 +95,11 @@ class MyComponent extends Component {
} }
} }
} }
return propsLoaded;
allFetchedDataReady(){
if (this.checkPropsHasError() ||this.checkPropsIsLoading() || this.checkPropsInvalidated() || this.checkPropsIsSaving()) {
return false;
} else {
return true;
} }
allFetchedDataAreReady() {
return this.loadPropsIfNeeded(true);
} }
componentDidMount() { componentDidMount() {
...@@ -130,7 +120,7 @@ class MyComponent extends Component { ...@@ -130,7 +120,7 @@ class MyComponent extends Component {
return <p>Sorry! There was an error loading the items</p>; return <p>Sorry! There was an error loading the items</p>;
} }
if (!this.allFetchedDataReady()) { if (!this.allFetchedDataAreReady()) {
return <Loading />; return <Loading />;
} }
......
...@@ -20,7 +20,7 @@ class ThemeProvider extends MyComponent { ...@@ -20,7 +20,7 @@ class ThemeProvider extends MyComponent {
state = { theme: this.props.themeSavedInTheApp.theme }; state = { theme: this.props.themeSavedInTheApp.theme };
myComponentDidUpdate() { myComponentDidUpdate() {
if (!this.allFetchedDataReady()) { if (!this.allFetchedDataAreReady()) {
return; return;
} }
......
...@@ -89,7 +89,7 @@ class ColorTool extends MyComponent { ...@@ -89,7 +89,7 @@ class ColorTool extends MyComponent {
} }
myComponentDidUpdate() { myComponentDidUpdate() {
if (!this.allFetchedDataReady()) { if (!this.allFetchedDataAreReady()) {
return return
} }
......
...@@ -88,11 +88,11 @@ class UniversityTemplate extends React.Component { ...@@ -88,11 +88,11 @@ class UniversityTemplate extends React.Component {
</Tabs> </Tabs>
</AppBar> </AppBar>
{value === 0 && <TabContainer> <GeneralInfoTab univId={univId} /> </TabContainer>} {value === 0 && <TabContainer> <GeneralInfoTab univId={univId} /> </TabContainer>}
{value === 1 && <TabContainer> <UniversityMoreTab univId={univId} /> </TabContainer>} {/* {value === 1 && <TabContainer> <UniversityMoreTab univId={univId} /> </TabContainer>} */}
{value === 2 && <TabContainer> <PreviousDeparturesTab univId={univId} /> </TabContainer>} {/* {value === 2 && <TabContainer> <PreviousDeparturesTab univId={univId} /> </TabContainer>} */}
{value === 3 && <TabContainer> <ScholarshipsTab univId={univId} /> </TabContainer>} {/* {value === 3 && <TabContainer> <ScholarshipsTab univId={univId} /> </TabContainer>} */}
{value === 4 && <TabContainer> <CampusesCitiesTab univId={univId} /> </TabContainer>} {/* {value === 4 && <TabContainer> <CampusesCitiesTab univId={univId} /> </TabContainer>} */}
{value === 5 && <TabContainer> <MoreTab univId={univId} /> </TabContainer>} {/* {value === 5 && <TabContainer> <MoreTab univId={univId} /> </TabContainer>} */}
</div> </div>
</div> </div>
......
import React from 'react'; import React from 'react';
import { withStyles } from '@material-ui/core/styles'; import PropTypes from 'prop-types';
import Tooltip from '@material-ui/core/Tooltip';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import classNames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import { compose } from 'recompose';
import { connect } from "react-redux";
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper'; import Paper from '@material-ui/core/Paper';
import Chip from '@material-ui/core/Chip';
import Avatar from '@material-ui/core/Avatar';
import Divider from '@material-ui/core/Divider';
import SettingsBackRestoreIcon from '@material-ui/icons/SettingsBackupRestore';
import CreateIcon from '@material-ui/icons/Create';
import VerifiedUserIcon from '@material-ui/icons/VerifiedUser';
import LinkIcon from '@material-ui/icons/Link';
import NotificationImportantIcon from '@material-ui/icons/NotificationImportant';
import FullScreenDialog from '../../shared/FullScreenDialog'; import FullScreenDialog from '../../shared/FullScreenDialog';
import MyBadge from '../../shared/MyBadge';
import red from '@material-ui/core/colors/red'; import red from '@material-ui/core/colors/red';
import orange from '@material-ui/core/colors/orange'; import orange from '@material-ui/core/colors/orange';
import green from '@material-ui/core/colors/green'; import green from '@material-ui/core/colors/green';
import MyComponent from '../../MyComponent';
import {
serverModerationStatusFetchData,
userDataElFetchData
} from '../../../generated/actions';
import renderUsefulLinks from './genericModuleFunctions/renderUsefulLinks';
import renderFirstRow from './genericModuleFunctions/renderFirstRow';
const styles = theme => ({ const styles = theme => ({
root: { root: {
flexGrow: 1, flexGrow: 1,
...@@ -63,7 +59,7 @@ const styles = theme => ({ ...@@ -63,7 +59,7 @@ const styles = theme => ({
chip: { chip: {
margin: theme.spacing.unit, margin: theme.spacing.unit,
}, },
titleContainer:{ titleContainer: {
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
}, },
...@@ -73,143 +69,9 @@ const styles = theme => ({ ...@@ -73,143 +69,9 @@ const styles = theme => ({
} }
}) })
function renderTitle() {
const { title, classes, importanceLevel } = this.props;
if (title) {
if (importanceLevel) {
let c = classNames(classes.titleIcon);
if (importanceLevel == "IMPORTANT"){
c = classNames(classes.titleIcon, classes.red)
} else if (importanceLevel == "important"){
c = classNames(classes.titleIcon, classes.orange)
}
return (
<div className={classes.titleContainer}>
<NotificationImportantIcon className={c} />
<Typography variant='display1'>{title}</Typography>
</div>
)
} else {
return (
<Typography variant='display1'>{title}</Typography>
)
}
} else {
return (<div></div>)
}
}
function renderUpdateInfo() {
const { automaticData } = this.props;
if (automaticData) {
return (
<Typography variant='caption'>Dernière mise à jour le 08/09/2018 à 15h20</Typography>
)
} else {
return (
<Typography variant='caption'>Mis à jour par <em>chehabfl</em> le 08/09/2018 à 15h20</Typography>
)
}
}
function renderFirstRow() {
const { classes, title, automaticData, theme } = this.props;
let classEdit = "green",
classModer = "orange",
classVersion = "red";
if (automaticData) {
classEdit = "disabled";
classModer = "disabled";
classVersion = "disabled";
}
return (
<Grid container spacing={8}>
<Grid item xs style={{paddingBottom: theme.spacing.unit}}>
{renderTitle.bind(this)()}
{renderUpdateInfo.bind(this)()}
</Grid>
<Grid item xs={4} style={{ textAlign: 'right' }}>
<Tooltip title="Informations sur la modération" placement="top">
<div style={{ display: 'inline-block' }}> {/* Needed to fire events for the tooltip when below is disabled! when below is disabled!! */}
<MyBadge classes={{ badge: classes.badge }} badgeContent={null} color="secondary">
<IconButton aria-label="Modération" disabled={automaticData} className={classes.button}>
<VerifiedUserIcon className={classes[classModer]} />
</IconButton>
</MyBadge>
</div>
</Tooltip>
<Tooltip title="Informations sur les possibilités d'éditions" placement="top">
<div style={{ display: 'inline-block' }}> {/* Needed to fire events for the tooltip when below is disabled!! */}
<IconButton aria-label="Éditer" className={classes.button} disabled={automaticData} onClick={this.handleClickOpenFullScreenDialog}>
<CreateIcon className={classes[classEdit]} />
</IconButton>
</div>
</Tooltip>
<Tooltip title="Informations sur les versions disponibles" placement="top">
<div style={{ display: 'inline-block' }}> {/* Needed to fire events for the tooltip when below is disabled!! */}
<MyBadge classes={{ badge: classes.badge }} badgeContent={40} color="secondary">
<IconButton aria-label="Restorer" disabled={automaticData} className={classes.button}>
<SettingsBackRestoreIcon className={classes[classVersion]} />
</IconButton>
</MyBadge>
</div>
</Tooltip>
</Grid>
</Grid>
)
}
function renderUsefulLinks() {
const { classes, usefulLinks, theme} = this.props;
const nbItems = usefulLinks.length;
if (nbItems == 0) {
return (<div></div>)
}
const s = nbItems > 1 ? "s" : "";
return (
<div>
<Typography variant='caption' style={{paddingTop:2*theme.spacing.unit}}> Source{s} :</Typography>
<div className={classes.rootLinks}>
{
usefulLinks.map((el, index) => {
return (
<Chip
key={index}
avatar={
<Avatar>
<LinkIcon />
</Avatar>
}
label={el.description}
className={classes.chip}
color="secondary"
onClick={() => open(el.url, '_blank')}
variant="outlined"
/>
)
})
}
</div> class GenericModule extends MyComponent {
</div>
)
}
class GenericModule extends React.Component {
state = { state = {
fullScreenDialogOpen: false, fullScreenDialogOpen: false,
}; };
...@@ -222,8 +84,9 @@ class GenericModule extends React.Component { ...@@ -222,8 +84,9 @@ class GenericModule extends React.Component {
this.setState({ fullScreenDialogOpen: false }); this.setState({ fullScreenDialogOpen: false });
}; };
render() { myRender() {
const { classes, title } = this.props; console.log(renderFirstRow)
const { classes } = this.props;
return ( return (
<div> <div>
<FullScreenDialog open={this.state.fullScreenDialogOpen} handleClose={this.handleCloseFullScreenDialog} /> <FullScreenDialog open={this.state.fullScreenDialogOpen} handleClose={this.handleCloseFullScreenDialog} />
...@@ -243,9 +106,34 @@ class GenericModule extends React.Component { ...@@ -243,9 +106,34 @@ class GenericModule extends React.Component {
GenericModule.defaultProps = { GenericModule.defaultProps = {