Commit dbed16cc authored by Florent Chehab's avatar Florent Chehab

New pendingModeration setup in frontend.

Fixes #55
parent 9ea5c94b
Pipeline #36195 failed with stages
in 4 minutes and 58 seconds
...@@ -5,6 +5,7 @@ from .pendingModeration import ( ...@@ -5,6 +5,7 @@ from .pendingModeration import (
PendingModeration, PendingModeration,
PendingModerationSerializer, PendingModerationSerializer,
PendingModerationViewSet, PendingModerationViewSet,
PendingModerationObjViewset,
) )
from .forTestingModeration import ( from .forTestingModeration import (
ForTestingModeration, ForTestingModeration,
......
...@@ -6,7 +6,7 @@ from django.db import models ...@@ -6,7 +6,7 @@ from django.db import models
from backend_app.custom import MySerializerWithJSON from backend_app.custom import MySerializerWithJSON
from backend_app.fields import JSONField from backend_app.fields import JSONField
from backend_app.utils import get_model_config, get_viewset_permissions from backend_app.utils import get_model_config, get_viewset_permissions
from rest_framework import serializers, viewsets from rest_framework import serializers, viewsets, mixins
class PendingModeration(models.Model): class PendingModeration(models.Model):
...@@ -56,3 +56,19 @@ class PendingModerationViewSet(viewsets.ModelViewSet): ...@@ -56,3 +56,19 @@ class PendingModerationViewSet(viewsets.ModelViewSet):
permission_classes = get_viewset_permissions("PendingModerationViewSet") permission_classes = get_viewset_permissions("PendingModerationViewSet")
queryset = PendingModeration.objects.all() # pylint: disable=E1101 queryset = PendingModeration.objects.all() # pylint: disable=E1101
serializer_class = PendingModerationSerializer serializer_class = PendingModerationSerializer
class PendingModerationObjViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
Viewset to retrieve the pending moderation data for an object
"""
permission_classes = get_viewset_permissions("PendingModerationObjViewset")
serializer_class = PendingModerationSerializer
def get_queryset(self):
content_type_id = self.kwargs["content_type_id"]
object_pk = self.kwargs["object_pk"]
return PendingModeration.objects.filter(
content_type=content_type_id, object_id=object_pk
)
import React from "react"; import React from "react";
import CustomComponentForAPI from "../../../common/CustomComponentForAPI";
import { openFullScreenDialog, closeFullScreenDialog } from "../../../../redux/actions/fullScreenDialog"; import { openFullScreenDialog, closeFullScreenDialog } from "../../../../redux/actions/fullScreenDialog";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
...@@ -22,7 +23,6 @@ import Loading from "../../../common/Loading"; ...@@ -22,7 +23,6 @@ import Loading from "../../../common/Loading";
import dateTimeStrToStr from "../../../../utils/dateTimeStrToStr"; import dateTimeStrToStr from "../../../../utils/dateTimeStrToStr";
import editorStyle from "../../editors/common/editorStyle"; import editorStyle from "../../editors/common/editorStyle";
import CustomComponentForAPI from "../../../common/CustomComponentForAPI";
import getActions from "../../../../redux/api/getActions"; import getActions from "../../../../redux/api/getActions";
...@@ -32,7 +32,7 @@ import getActions from "../../../../redux/api/getActions"; ...@@ -32,7 +32,7 @@ import getActions from "../../../../redux/api/getActions";
* Component to display the previous versions of module * Component to display the previous versions of module
* *
* @class History * @class History
* @extends {Component} * @extends {CustomComponentForAPI}
*/ */
class History extends CustomComponentForAPI { class History extends CustomComponentForAPI {
// Store the version that is being viewed // Store the version that is being viewed
...@@ -217,10 +217,6 @@ class History extends CustomComponentForAPI { ...@@ -217,10 +217,6 @@ class History extends CustomComponentForAPI {
* @memberof History * @memberof History
*/ */
render() { render() {
if (!this.allApiDataIsReady()) {
return <></>;
}
return <></>; return <></>;
} }
} }
......
...@@ -54,7 +54,7 @@ class ModuleWrapper extends Component { ...@@ -54,7 +54,7 @@ class ModuleWrapper extends Component {
* @memberof ModuleWrapper * @memberof ModuleWrapper
*/ */
openEditorPanel(ignorePendingModeration = false) { openEditorPanel(ignorePendingModeration = false) {
if (ignorePendingModeration || this.props.rawModelData.pending_moderation.length == 0) { if (ignorePendingModeration || !this.props.rawModelData.has_pending_moderation) {
this.setState({ editorOpen: true }); this.setState({ editorOpen: true });
} else { } else {
this.alertThereIsSomethingPendingModeration(); this.alertThereIsSomethingPendingModeration();
...@@ -246,6 +246,7 @@ class ModuleWrapper extends Component { ...@@ -246,6 +246,7 @@ class ModuleWrapper extends Component {
{this.state.pendingModerationOpen ? {this.state.pendingModerationOpen ?
<PendingModeration <PendingModeration
renderer={this} renderer={this}
modelInfo={{ contentTypeId: rawModelData.content_type_id, id: rawModelData.id }}
closePendingModerationPanel={() => this.closePendingModerationPanel()} closePendingModerationPanel={() => this.closePendingModerationPanel()}
editFromPendingModeration={(pendingModelData) => this.editFromPendingModeration(pendingModelData)} editFromPendingModeration={(pendingModelData) => this.editFromPendingModeration(pendingModelData)}
moderatePendingModeration={(modelData) => this.moderatePendingModeration(modelData)} moderatePendingModeration={(modelData) => this.moderatePendingModeration(modelData)}
......
import React, { Component } from "react"; import React from "react";
import CustomComponentForAPI from "../../../common/CustomComponentForAPI";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { openFullScreenDialog, closeFullScreenDialog } from "../../../../redux/actions/fullScreenDialog"; import { openFullScreenDialog, closeFullScreenDialog } from "../../../../redux/actions/fullScreenDialog";
...@@ -13,35 +14,37 @@ import IconButton from "@material-ui/core/IconButton"; ...@@ -13,35 +14,37 @@ import IconButton from "@material-ui/core/IconButton";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import Divider from "@material-ui/core/Divider"; import Divider from "@material-ui/core/Divider";
import getActions from "../../../../redux/api/getActions";
import editorStyle from "../../editors/common/editorStyle"; import editorStyle from "../../editors/common/editorStyle";
import Loading from "../../../common/Loading";
const styles = theme => ({
...editorStyle(theme),
editButton: {
display: "block",
marginLeft: "auto",
marginRight: "auto",
marginTop: 2 * theme.spacing.unit,
marginBottom: 2 * theme.spacing.unit
}
});
/** /**
* Class to handle models that are pending moderation. * Class to handle models that are pending moderation.
* *
* @class PendingModeration * @class PendingModeration
* @extends {Component} * @extends {CustomComponentForAPI}
*/ */
class PendingModeration extends Component { class PendingModeration extends CustomComponentForAPI {
constructor(props) {
super(props);
const { contentTypeId, id } = props.modelInfo;
// set the __apiAttr here so that we can read the correct data
this.__apiParam = {
pendingModeration: { contentTypeId, id }
};
}
/** /**
* Force update to force call of componentDidUpdate en mount and force the rendering of the moderation panel. * Force update to force call of componentDidUpdate en mount and force the rendering of the moderation panel.
* *
* @memberof PendingModeration * @memberof PendingModeration
*/ */
componentDidMount(){ componentDidMount() {
super.componentDidMount();
this.forceUpdate(); this.forceUpdate();
} }
...@@ -50,8 +53,9 @@ class PendingModeration extends Component { ...@@ -50,8 +53,9 @@ class PendingModeration extends Component {
* *
* @memberof PendingModeration * @memberof PendingModeration
*/ */
componentDidUpdate() { componentDidUpdate(prevProps, prevState, snapshot) {
this.props.openFullScreenDialog(this.renderPendingModerationPanel()); super.componentDidUpdate(prevProps, prevState, snapshot);
this.props.openFullScreenDialog(this.renderPanel());
} }
/** /**
...@@ -61,11 +65,11 @@ class PendingModeration extends Component { ...@@ -61,11 +65,11 @@ class PendingModeration extends Component {
* @memberof PendingModeration * @memberof PendingModeration
*/ */
getRawModelDataFromPending() { getRawModelDataFromPending() {
const { renderer } = this.props; const pendingObject = this.getLatestReadData("pendingModeration")[0];
let pendingRawModelData = Object.assign({}, renderer.props.rawModelData.pending_moderation[0].new_object); return Object.assign(
// we have to copy the original ID {},
pendingRawModelData.id = renderer.props.rawModelData.id; pendingObject.new_object,
return pendingRawModelData; { id: pendingObject.object_id });
} }
/** /**
...@@ -112,6 +116,7 @@ class PendingModeration extends Component { ...@@ -112,6 +116,7 @@ class PendingModeration extends Component {
closePanel() { closePanel() {
this.props.closeFullScreenDialog(); this.props.closeFullScreenDialog();
this.props.closePendingModerationPanel(); this.props.closePendingModerationPanel();
this.props.resetPendingModeration();
} }
/** /**
...@@ -120,7 +125,11 @@ class PendingModeration extends Component { ...@@ -120,7 +125,11 @@ class PendingModeration extends Component {
* @returns * @returns
* @memberof PendingModeration * @memberof PendingModeration
*/ */
renderPendingModerationPanel() { renderPanel() {
if (!this.allApiDataIsReady()) {
return <Loading />;
}
const { classes } = this.props, const { classes } = this.props,
pendingModelData = this.getRawModelDataFromPending(); pendingModelData = this.getRawModelDataFromPending();
...@@ -146,6 +155,12 @@ class PendingModeration extends Component { ...@@ -146,6 +155,12 @@ class PendingModeration extends Component {
); );
} }
/**
* @override
*
* @returns
* @memberof PendingModeration
*/
render() { render() {
return <></>; return <></>;
} }
...@@ -153,6 +168,7 @@ class PendingModeration extends Component { ...@@ -153,6 +168,7 @@ class PendingModeration extends Component {
PendingModeration.propTypes = { PendingModeration.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
modelInfo: PropTypes.object.isRequired,
editFromPendingModeration: PropTypes.func.isRequired, editFromPendingModeration: PropTypes.func.isRequired,
closePendingModerationPanel: PropTypes.func.isRequired, closePendingModerationPanel: PropTypes.func.isRequired,
moderatePendingModeration: PropTypes.func.isRequired, moderatePendingModeration: PropTypes.func.isRequired,
...@@ -167,15 +183,38 @@ PendingModeration.defaultProps = { ...@@ -167,15 +183,38 @@ PendingModeration.defaultProps = {
closePendingModerationPanel: () => console.error("Dev forgot something...") closePendingModerationPanel: () => console.error("Dev forgot something...")
}; };
const mapStateToProps = (state) => {
return {
pendingModeration: state.api.pendingModerationObjSpecific
};
};
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return { return {
api: {
pendingModeration: (param) => dispatch(getActions("pendingModerationObj").readSpecific(`${param.contentTypeId}/${param.id}`)),
},
resetPendingModeration: () => dispatch(getActions("pendingModerationObj").setInvalidatedSpecific(true)),
openFullScreenDialog: (innerNodes) => dispatch(openFullScreenDialog(innerNodes)), openFullScreenDialog: (innerNodes) => dispatch(openFullScreenDialog(innerNodes)),
closeFullScreenDialog: () => dispatch(closeFullScreenDialog()), closeFullScreenDialog: () => dispatch(closeFullScreenDialog()),
}; };
}; };
const styles = theme => ({
...editorStyle(theme),
editButton: {
display: "block",
marginLeft: "auto",
marginRight: "auto",
marginTop: 2 * theme.spacing.unit,
marginBottom: 2 * theme.spacing.unit
}
});
export default compose( export default compose(
withStyles(styles, { withTheme: true }), withStyles(styles, { withTheme: true }),
connect(() => Object(), mapDispatchToProps) connect(mapStateToProps, mapDispatchToProps)
)(PendingModeration); )(PendingModeration);
export default function getModerationTooltipAndClass(nbPendingModeration, readOnly) { export default function getModerationTooltipAndClass(hasPendingModeration, readOnly) {
if (readOnly) { if (readOnly) {
return { return {
moderTooltip: "Ce contenu n'est pas concerné par la modération.", moderTooltip: "Ce contenu n'est pas concerné par la modération.",
...@@ -6,20 +6,15 @@ export default function getModerationTooltipAndClass(nbPendingModeration, readOn ...@@ -6,20 +6,15 @@ export default function getModerationTooltipAndClass(nbPendingModeration, readOn
}; };
} }
if (nbPendingModeration == 0) { if (hasPendingModeration) {
return {
moderTooltip: "Aucune mise-à-jour de ce module est en attente de modération pour ce module.",
moderClass: "green"
};
} else if (nbPendingModeration == 1) {
return { return {
moderTooltip: "Une mise-à-jour de ce modèle est en attente de modération.", moderTooltip: "Une mise-à-jour de ce modèle est en attente de modération.",
moderClass: "orange" moderClass: "orange"
}; };
} else { } else {
return { return {
moderTooltip: "Plusieurs mises à jour de ce modèle sont en attente.", moderTooltip: "Aucune mise-à-jour de ce module est en attente de modération pour ce module.",
moderClass: "orange" moderClass: "green"
}; };
} }
} }
...@@ -19,16 +19,11 @@ import Tooltip from "@material-ui/core/Tooltip"; ...@@ -19,16 +19,11 @@ import Tooltip from "@material-ui/core/Tooltip";
export default function renderFirstRow(userCanModerate) { export default function renderFirstRow(userCanModerate) {
const { classes, theme, rawModelData } = this.props, const { classes, theme, rawModelData } = this.props,
nbVersions = Math.max(0, rawModelData.nb_versions), nbVersions = Math.max(0, rawModelData.nb_versions),
{ pending_moderation } = rawModelData; hasPendingModeration = rawModelData.has_pending_moderation;
let nbPendingModeration = 0;
if (pending_moderation) {
nbPendingModeration = pending_moderation.length;
}
const readOnly = rawModelData.model_config.read_only, const readOnly = rawModelData.model_config.read_only,
{ versionTooltip, versionClass } = getVersionTooltipAndClass(nbVersions), { versionTooltip, versionClass } = getVersionTooltipAndClass(nbVersions),
{ moderTooltip, moderClass } = getModerationTooltipAndClass(nbPendingModeration, readOnly), { moderTooltip, moderClass } = getModerationTooltipAndClass(hasPendingModeration, readOnly),
{ editTooltip, editClass } = getEditTooltipAndClass(readOnly, userCanModerate); { editTooltip, editClass } = getEditTooltipAndClass(readOnly, userCanModerate);
return ( return (
...@@ -41,7 +36,7 @@ export default function renderFirstRow(userCanModerate) { ...@@ -41,7 +36,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={moderTooltip} placement="top"> <Tooltip title={moderTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled! when below is disabled!! */} <div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled! when below is disabled!! */}
<MyBadge badgeContent={nbPendingModeration} color="secondary" minNumber={1}> <MyBadge badgeContent={hasPendingModeration ? 1 : 0} color="secondary" minNumber={1}>
<IconButton aria-label="Modération" disabled={moderClass == "disabled" || moderClass == "green"} onClick={() => this.openPendingModerationPanel()} className={classes.button}> <IconButton aria-label="Modération" disabled={moderClass == "disabled" || moderClass == "green"} onClick={() => this.openPendingModerationPanel()} className={classes.button}>
<VerifiedUserIcon className={classes[moderClass]} /> <VerifiedUserIcon className={classes[moderClass]} />
</IconButton> </IconButton>
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
# - IsOwner : (or ) # - IsOwner : (or )
# #
# If you have a custom get_queryset function in your viewset, you need to specify an api_name
##################################################### #####################################################
## Custom Viewsets that doesn't have a model behind ## Custom Viewsets that doesn't have a model behind
...@@ -252,6 +254,14 @@ ...@@ -252,6 +254,14 @@
viewset_permission: IsStaffOrReadOnly viewset_permission: IsStaffOrReadOnly
ignore_in_admin: true ignore_in_admin: true
- viewset: PendingModerationObjViewset
import_location: abstract.my_model
api_end_point: pendingModerationObj
api_attr: (?P<content_type_id>[0-9]+)/(?P<object_pk>[0-9A-Za-z]+)
api_name: pendingModerationObj
read_only: true
ignore_in_admin: true
- model: ForTestingModeration - model: ForTestingModeration
viewset: ForTestingModerationViewSet viewset: ForTestingModerationViewSet
import_location: abstract.my_model import_location: abstract.my_model
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment