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

fixing issue unresolve bug and adding concept of scanIssue/decreased … #873

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 171 additions & 97 deletions assets/js/Components/UfixitModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class UfixitModal extends React.Component {
this.state = {
windowContents: 'preview',
expandExample: false,
issueDetailsPending: true
}

this.modalMessages = []
Expand All @@ -46,6 +47,22 @@ class UfixitModal extends React.Component {
this.handleManualScan = this.handleManualScan.bind(this)
}

componentDidMount() {
this.syncIssue(this.props.activeIssue)
}

componentDidUpdate(prevProps, prevState) {
if (prevProps.activeIssue.id != this.props.activeIssue.id) {
this.setState({
windowContents: 'preview',
expandExample: false,
issueDetailsPending: true
})

this.syncIssue(this.props.activeIssue)
}
}

findActiveIndex() {
if (this.props.filteredRows && this.props.activeIssue) {
for (const i in this.props.filteredRows) {
Expand All @@ -69,6 +86,7 @@ class UfixitModal extends React.Component {
newIndex = 0
}
this.clearMessages()
this.setState({ issueDetailsPending: true })
this.props.handleActiveIssue(this.props.filteredRows[newIndex].issue, newIndex)
}

Expand All @@ -87,14 +105,18 @@ class UfixitModal extends React.Component {
const pending = (this.props.activeIssue && (this.props.activeIssue.pending == '1'))

let activeIndex = this.findActiveIndex();
const UfixitForm = returnIssueForm(activeIssue)

let code = ''
let UfixitForm
let showExample = false
if (!this.props.t(`rule.example.${activeIssue.scanRuleId}`).includes('rule.example')) {
showExample = true
}

let code = this.prepareCode(activeIssue)
if (!this.state.issueDetailsPending) {
UfixitForm = ufixitService.returnIssueForm(activeIssue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot find ufixitService anywhere in the React files, only on this line. Returns an error:

Uncaught (in promise) ReferenceError: ufixitService is not defined
    at a.value (app.js:252:268294)
    at Pa (app.js:377:69887)
    at Na (app.js:377:69682)
    at gs (app.js:377:105481)
    at cl (app.js:377:96728)
    at sl (app.js:377:96653)
    at Zs (app.js:377:93683)
    at app.js:377:45325
    at t.unstable_runWithPriority (app.js:11:3844)
    at Fi (app.js:377:45034)

code = this.prepareCode(activeIssue)
}

return (
<View>
Expand All @@ -120,96 +142,103 @@ class UfixitModal extends React.Component {
</Modal.Header>
<Modal.Body padding="small medium">
<MessageTray messages={this.modalMessages} clearMessages={this.clearMessages} t={this.props.t} hasNewReport={true} />
<View as="div" margin="small">
<View as="div" margin="small 0">
<Text lineHeight="default">
{ReactHtmlParser(this.props.t(`rule.desc.${activeIssue.scanRuleId}`), { preprocessNodes: (nodes) => Html.processStaticHtml(nodes, this.props.settings) })}
</Text>
</View>
{showExample &&
<ToggleDetails
summary={this.state.expandExample ? (this.props.t('label.btn.hide_example')) : (this.props.t('label.btn.show_example'))}
expanded={this.state.expandExample}
fluidWidth={true}
onToggle={this.handleExampleToggle}>
<View as="div" margin="small 0">
{ReactHtmlParser(this.props.t(`rule.example.${activeIssue.scanRuleId}`), { preprocessNodes: (nodes) => Html.processStaticHtml(nodes, this.props.settings) })}
</View>
</ToggleDetails>
}
</View>
<Flex justifyItems="space-between" alignItems="start">
<Flex.Item width="46%" padding="0">
<View as="div">
<UfixitForm activeIssue={activeIssue} t={this.props.t} settings={this.props.settings}
handleIssueSave={this.handleIssueSave}
addMessage={this.addMessage}
handleActiveIssue={this.props.handleActiveIssue}
handleManualScan={this.handleManualScan} />
{this.state.issueDetailsPending &&
<View as="div" textAlign="center" padding="large">
<Spinner renderTitle="Scanning" size="large" />
</View>
{('module' !== activeContentItem.contentType) &&
<View as="div" background="secondary" padding="medium" margin="small 0 0 x-small">
<Text as="div" weight="bold">{this.props.t('label.manual_resolution')}</Text>
<Text as="div" lineHeight="default">{this.props.t('label.resolved_description')}</Text>
<View as="div" padding="small 0 0 0">
{('2' == activeIssue.pending) ?
<Spinner renderTitle={this.props.t('form.processing')} size="x-small" />
:
<Checkbox onChange={this.handleIssueResolve} label={this.props.t('label.mark_resolved')}
checked={(activeIssue.status == '2')} disabled={(activeIssue.status == '1')} />
}
}
{!this.state.issueDetailsPending && <View as="div">
<View as="div" margin="small">
<View as="div" margin="small 0">
<Text lineHeight="default">
{ReactHtmlParser(this.props.t(`rule.desc.${activeIssue.scanRuleId}`), { preprocessNodes: (nodes) => Html.processStaticHtml(nodes, this.props.settings) })}
</Text>
</View>
{showExample &&
<ToggleDetails
summary={this.state.expandExample ? (this.props.t('label.btn.hide_example')) : (this.props.t('label.btn.show_example'))}
expanded={this.state.expandExample}
fluidWidth={true}
onToggle={this.handleExampleToggle}>
<View as="div" margin="small 0">
{ReactHtmlParser(this.props.t(`rule.example.${activeIssue.scanRuleId}`), { preprocessNodes: (nodes) => Html.processStaticHtml(nodes, this.props.settings) })}
</View>
</View>
</ToggleDetails>
}
</Flex.Item>
<Flex.Item width="50%" padding="0" overflowY="auto">
<View as="div" padding="x-small">
<InlineList delimiter="pipe">
<InlineList.Item>
{('preview' === this.state.windowContents) ?
<Text weight="bold">{this.props.t('label.preview')}</Text>
:
<Link isWithinText={false} onClick={() => this.handleWindowToggle('preview')}>
{this.props.t('label.preview')}</Link>
</View>
<Flex justifyItems="space-between" alignItems="start">
<Flex.Item width="46%" padding="0">
<View as="div">
<UfixitForm activeIssue={activeIssue} t={this.props.t} settings={this.props.settings}
handleIssueSave={this.handleIssueSave}
addMessage={this.addMessage}
handleActiveIssue={this.props.handleActiveIssue}
handleManualScan={this.handleManualScan} />
</View>
{('module' !== activeContentItem.contentType) &&
<View as="div" background="secondary" padding="medium" margin="small 0 0 x-small">
<Text as="div" weight="bold">{this.props.t('label.manual_resolution')}</Text>
<Text as="div" lineHeight="default">{this.props.t('label.resolved_description')}</Text>
<View as="div" padding="small 0 0 0">
{('2' == activeIssue.pending) ?
<Spinner renderTitle={this.props.t('form.processing')} size="x-small" />
:
<Checkbox onChange={this.handleIssueResolve} label={this.props.t('label.mark_resolved')}
checked={(activeIssue.status == '2')} disabled={(activeIssue.status == '1')} />
}
</View>
</View>
}
</Flex.Item>
<Flex.Item width="50%" padding="0" overflowY="auto">
<View as="div" padding="x-small">
<InlineList delimiter="pipe">
<InlineList.Item>
{('preview' === this.state.windowContents) ?
<Text weight="bold">{this.props.t('label.preview')}</Text>
:
<Link isWithinText={false} onClick={() => this.handleWindowToggle('preview')}>
{this.props.t('label.preview')}</Link>
}
</InlineList.Item>
<InlineList.Item>
{('html' === this.state.windowContents) ?
<Text weight="bold">{this.props.t('label.view_source')}</Text>
:
<Link isWithinText={false} onClick={() => this.handleWindowToggle('html')}>
{this.props.t('label.view_source')}</Link>
}
</InlineList.Item>
</InlineList>
<View as="div" shadow="resting" padding="small" margin="x-small 0 0 0" height="200px" overflowY="auto">
{('preview' === this.state.windowContents) &&
<Preview
activeIssue={this.props.activeIssue}
settings={this.props.settings}
>
</Preview>
}
</InlineList.Item>
<InlineList.Item>
{('html' === this.state.windowContents) ?
<Text weight="bold">{this.props.t('label.view_source')}</Text>
:
<Link isWithinText={false} onClick={() => this.handleWindowToggle('html')}>
{this.props.t('label.view_source')}</Link>
{('html' === this.state.windowContents) &&
<CodeEditor margin="x-small 0" label={this.props.t('label.code_preview')} language="html" readOnly={true}
value={code}
options={{ lineNumbers: true }} />
}
</InlineList.Item>
</InlineList>
<View as="div" shadow="resting" padding="small" margin="x-small 0 0 0" height="200px" overflowY="auto">
{('preview' === this.state.windowContents) &&
<Preview
activeIssue={this.props.activeIssue}
settings={this.props.settings}
>
</Preview>
}
{('html' === this.state.windowContents) &&
<CodeEditor margin="x-small 0" label={this.props.t('label.code_preview')} language="html" readOnly={true}
value={code}
options={{ lineNumbers: true }} />
</View>
</View>
<View as="div" padding="0 x-small">
{/* <Text weight="bold">{this.props.t('label.source')}</Text> */}
{activeContentItem &&
<View as="div">
<Pill>{activeContentItem.contentType}</Pill>
<Link onClick={this.handleOpenContent} isWithinText={false} margin="small" renderIcon={<IconExternalLinkLine />} iconPlacement="end">
{activeContentItem.title}
</Link>
</View>
}
</View>
</View>
<View as="div" padding="0 x-small">
{/* <Text weight="bold">{this.props.t('label.source')}</Text> */}
{activeContentItem &&
<View as="div">
<Pill>{activeContentItem.contentType}</Pill>
<Link onClick={this.handleOpenContent} isWithinText={false} margin="small" renderIcon={<IconExternalLinkLine />} iconPlacement="end">
{activeContentItem.title}
</Link>
</View>
}
</View>
</Flex.Item>
</Flex>
</Flex.Item>
</Flex>
</View>}
</Modal.Body>

<Modal.Footer>
Expand Down Expand Up @@ -280,7 +309,12 @@ class UfixitModal extends React.Component {

if (activeIssue.status) {
activeIssue.status = false
activeIssue.newHtml = Html.toString(Html.removeClass(activeIssue.sourceHtml, 'phpally-ignore'))
activeIssue.newHtml = Html.toString(
Html.removeClass(activeIssue.sourceHtml, 'phpally-ignore')
)
activeIssue.newHtml = Html.toString(
Html.addClass(activeIssue.newHtml, `udoit-${activeIssue.id}`)
)
}
else {
activeIssue.status = 2
Expand All @@ -296,24 +330,46 @@ class UfixitModal extends React.Component {

if (response.data.issue) {
const newIssue = { ...activeIssue, ...response.data.issue }
const newReport = response.data.report

// update activeIssue
newIssue.pending = false
newIssue.recentlyResolved = !!activeIssue.status
newIssue.sourceHtml = newIssue.newHtml
newIssue.newHtml = ''
// Get updated report
api.scanContent(newIssue.contentItemId)
.then((responseStr) => responseStr.json())
.then((res) => {
// update activeIssue
this.props.handleActiveIssue(newIssue)

this.props.handleIssueSave(newIssue, res.data)
})
}
else {
api
.scanContent(newIssue.contentItemId)
.then((responseStr) => responseStr.json())
.then((scanResponse) => {
// If we just unresolved the issue
if (!newIssue.status) {
// Loop through report
for (var issue of scanResponse.data.issues) {
if (issue.scanRuleId === newIssue.scanRuleId) {
// Get issue details
api.syncIssue(issue)
.then((responseStr) => responseStr.json())
.then((res) => {
let issueDetails = res.data

if (issueDetails.sourceHtml === newIssue.sourceHtml) {
newIssue.id = issueDetails.id
newIssue.sourceHtml = Html.toString(
Html.removeClass(newIssue.sourceHtml, `udoit-${activeIssue.id}`)
)
newIssue.newHtml = ''
newIssue.previewHtml = issueDetails.previewHtml
}
})
}
}
}

this.props.handleActiveIssue(newIssue)
this.props.handleIssueSave(newIssue, scanResponse.data)

})
} else {
activeIssue.pending = false
this.props.handleActiveIssue(activeIssue)
}
Expand Down Expand Up @@ -432,6 +488,24 @@ class UfixitModal extends React.Component {
clearMessages() {
this.modalMessages = [];
}

syncIssue(issue) {
let api = new Api(this.props.settings)

api.syncIssue(issue)
.then((response) => response.json())
.then((response) => {
const newIssue = { ...issue, ...response.data }

// set messages
response.messages.forEach((msg) => this.addMessage(msg))

if (newIssue.id === this.props.activeIssue.id) {
this.props.handleActiveIssue(newIssue)
this.setState({ issueDetailsPending: false });
}
})
}
}

export default UfixitModal;
16 changes: 16 additions & 0 deletions assets/js/Services/Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default class Api {
getReport: '/api/courses/{course}/reports/{report}',
getReportHistory: '/api/courses/{course}/reports',
saveIssue: '/api/issues/{issue}/save',
syncIssue: '/api/issues/{issue}',
resolveIssue: '/api/issues/{issue}/resolve',
reviewFile: '/api/files/{file}/review',
postFile: '/api/files/{file}/post',
Expand Down Expand Up @@ -90,6 +91,21 @@ export default class Api {
})
}

syncIssue(issue) {
const authToken = this.getAuthToken();

let url = `${this.apiUrl}${this.endpoints.syncIssue}`;
url = url.replace('{issue}', issue.id);

return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-AUTH-TOKEN': authToken,
},
});
}

resolveIssue(issue) {
const authToken = this.getAuthToken()

Expand Down
16 changes: 16 additions & 0 deletions src/Controller/IssuesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ public function __construct(ManagerRegistry $doctrine)
$this->doctrine = $doctrine;
}

#[Route(path: '/api/issues/{issue}', name: 'get_issue')]
public function getIssue(Issue $issue)
{
$apiResponse = new ApiResponse();

$apiResponse->setData([
'id' => $issue->getId(),
'sourceHtml' => $issue->getHtml(),
'previewHtml' => $issue->getPreviewHtml(),
'metadata' => $issue->getMetadata(),
'status' => $issue->getStatus(),
]);

return new JsonResponse($apiResponse);
}

// Save change to issue HTML to LMS
#[Route('/api/issues/{issue}/save', name: 'save_issue')]
public function saveIssue(
Expand Down
Loading