Skip to content

Commit

Permalink
Merge branch 'develop' into feat/maddy_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MaddyUnderStars authored Jul 25, 2024
2 parents 1db3f76 + 823e3ca commit ee74617
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 517 deletions.
16 changes: 11 additions & 5 deletions system/modules/admin/actions/userdel.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
<?php
function userdel_GET(Web $w) {
$w->pathMatch("id");
$user = AuthService::getInstance($w)->getObject("User",$w->ctx("id"));
$user = AuthService::getInstance($w)->getObject("User", $w->ctx("id"));
if ($user) {
$user->delete();
$w->msg("User ".$user->login." deleted.","/admin/users");

if ($w->session('user_id') == $w->ctx("id")) {
// We deleted our own user, force logout
$w->sessionDestroy();
$w->redirect($w->localUrl("/auth/login"));
} else {
$w->msg("User " . $user->login . " deleted.", "/admin/users");
}
} else {
$w->error("User ".$w->ctx("id")." does not exist.","/admin/users");
$w->error("User " . $w->ctx("id") . " does not exist.", "/admin/users");
}

}
}
114 changes: 61 additions & 53 deletions system/modules/search/templates/index.tpl.php
Original file line number Diff line number Diff line change
@@ -1,74 +1,82 @@
<div>
<h3 class="subheading columns large-6">Search</h3>
<span class="columns large-6" style="text-align: right;">
<p style="font-size: 12px;">
<strong>Note:</strong> Search terms must contain minimum 3 characters.
<br>
<strong>Tip:</strong> To search by Id, use 'id##' eg. id5.
</p>
</span>
<h3 class="subheading columns large-6">Search</h3>
<span class="columns large-6" style="text-align: right;">
<p style="font-size: 12px;">
<strong>Note:</strong> Search terms must contain minimum 3 characters.
<br>
<strong>Tip:</strong> To search by Id, use 'id##' eg. id5.
</p>
</span>
</div>
<hr>
<div class="row-fluid">
<!-- <form action="<?php // echo $webroot; ?>/search/results" method="GET">-->
<form id="search_form" class="clearfix">
<input type="hidden" name="<?php echo CSRF::getTokenID(); ?>" value="<?php echo CSRF::getTokenValue(); ?>" />
<div class="row-fluid">
<div class="small-12 medium-6 columns">
<input class="input-large" type="text" name="q" id="q" autofocus/>
</div>
<!-- <form action="<?php // echo $webroot;
?>/search/results" method="GET">-->
<form id="search_form" class="clearfix">
<input type="hidden" name="<?php echo CSRF::getTokenID(); ?>" value="<?php echo CSRF::getTokenValue(); ?>" />
<div class="row-fluid">
<div class="small-12 medium-6 columns">
<input class="input-large" type="text" name="q" id="q" autofocus />
</div>
<div class="small-12 medium-2 columns">
<?php echo Html::select("idx", $indexes); ?>
</div>
<div class="small-12 medium-2 columns">
<?php echo Html::select("tags", $tags); ?>
</div>
<div class="small-12 medium-2 columns">
<button class="button tiny small-12" type="submit">Go</button>
</div>
</div>
</form>
<?php echo Html::select("idx", $indexes); ?>
</div>
<div class="small-12 medium-2 columns">
<?php echo Html::select("tags", $tags); ?>
</div>
<div class="small-12 medium-2 columns">
<button class="button tiny small-12" type="submit">Go</button>
</div>
</div>
</form>


</div>

<div id="search_message" class="row hide">
<div data-alert class="alert-box warning" id="message_box"></div>
<div id="search_message" class="row">
<div data-alert style="margin-top: 1rem" class="alert-box warning" id="message_box"></div>
</div>

<div id="result" class="row" style="display: none;">

</div>

<script>
$("#search_form").submit(function(event) {
event.preventDefault();
$("#search_message").hide();
$("#result").hide();
const setError = (str) => {
if (!str) {
document.querySelector("#search_message").style.display = "none";
document.querySelector("#result").style.display = "block";
return;
}

document.querySelector("#message_box").innerText = str;
document.querySelector("#search_message").style.display = "block";
document.querySelector("#result").style.display = "none";
}

document.querySelector("#search_form").addEventListener("submit", async function(event) {
event.preventDefault();

setError(false);

const form = new FormData(event.target);
const body = new URLSearchParams(form);

try {
const response = await fetch(`/search/results?` + body.toString());

var data = $("#search_form").serialize();
const json = await response.json();

$.getJSON("/search/results", data,
function(response) {
if (response.success === false) {
$("#message_box").html(response.data);
$("#search_message").show();
} else {
var text_data = "<span style='padding-left: 20px;'>No results found</span>";
if (response.data) {
text_data = response.data;
}
$("#result").html(text_data).delay(100).fadeIn();
}
},
function(response) {
$("#message_box").html("Failed to receive a response from search");
$("#search_message").show();
}
);
if (!json.success)
return setError(json.data);

return false;
});
document.querySelector("#result").innerHTML =
json.data || `<span style="padding-left: 20px;">No results found</span>`;
} catch (e) {
setError(`Failed to receive a response from search`);
}

return false;
});
</script>
<br>
<br>
10 changes: 8 additions & 2 deletions system/modules/timelog/actions/edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,23 @@ function edit_POST(Web $w)
$timelogs_for_task_and_user[] = $existing_timelog;
}
}

$dedupeInfo = "current( id( $timelog->user_id ) dt_start( " . substr($timelog->dt_start, 0, 10) . " " . substr($timelog->dt_start, 11, 5) . " ) )";

foreach ($timelogs_for_task_and_user as $existing_timelog_for_task_and_user) {

$dedupeInfo .= " existing( id( $existing_timelog_for_task_and_user->user_id ) dt_start( " . gmdate('Y-m-d', strtotime($existing_timelog_for_task_and_user->getDateStart() . ' ' . $existing_timelog_for_task_and_user->getTimeStart())) . " " . gmdate('H:i', strtotime($existing_timelog_for_task_and_user->getTimeStart())) . " ) )";

if (gmdate('Y-m-d', strtotime($existing_timelog_for_task_and_user->getDateStart() . ' ' . $existing_timelog_for_task_and_user->getTimeStart())) == substr($timelog->dt_start, 0, 10) && gmdate('H:i', strtotime($existing_timelog_for_task_and_user->getTimeStart())) == substr($timelog->dt_start, 11, 5)) {
$w->error('Duplicate Timelog Removed', $redirect ?: '/timelog');
}
}

// Timelog user_id handled in insert/update
$timelog->insertOrUpdate();

// Save comment
$timelog->setComment($_POST['description']);

$w->msg("<div id='saved_record_id' data-id='" . $timelog->id . "' >Timelog saved</div>", (!empty($redirect) ? $redirect . "#timelog" : "/timelog"));
$w->msg("<div id='saved_record_id' data-id='" . $timelog->id . "' >Timelog saved<!--$dedupeInfo--></div>", (!empty($redirect) ? $redirect . "#timelog" : "/timelog"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ test("Test that duplicate timelogs are deleted" , async ({page}) => {
DateTime.fromFormat("6/6/2024", "d/M/yyyy"),
"1:00",
"2:00",
true,
timelog1
true
);

await TimelogHelper.deleteTimelog(page, timelog1, task, taskID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class TimelogHelper {
await expect(page.getByText(timelog)).toBeVisible();
}

static async createTimelog(page: Page, timelog: string, taskName: string, taskID: string, date: DateTime, start_time: string, end_time: string, check_duplicate: boolean = false, develop_ci_other_timelog: string = "")
static async createTimelog(page: Page, timelog: string, taskName: string, taskID: string, date: DateTime, start_time: string, end_time: string, check_duplicate: boolean = false)
{
if(page.url() != HOST + "/task/edit/" + taskID + "#details") {
if(page.url() != HOST + "/task/tasklist")
Expand All @@ -54,27 +54,17 @@ export class TimelogHelper {
await page.getByLabel("Description", {exact: true}).fill(timelog);
await page.locator("#timelog_edit_form").getByRole("button", { name: "Save" }).click();

if(await page.$("#saved_record_id") != null)
console.log(await page.$eval("#saved_record_id", el => el.innerHTML));

await page.getByRole("link", {name: taskName, exact: false}).first().click();
await page.getByRole("link", {name: "Time Log"}).click();
await page.reload();

if (check_duplicate) {
const otherTimelogPreElement = await page.$(`tr > td > pre:has-text("${develop_ci_other_timelog}")`);
const otherTimelogTrElement = await otherTimelogPreElement.evaluateHandle(node => node.closest('tr'));
const otherTimelogTrHtmlContent = await otherTimelogTrElement.innerHTML();
console.log(otherTimelogTrHtmlContent);

const thisTimelogPreElement = await page.$(`tr > td > pre:has-text("${timelog}")`);
if(thisTimelogPreElement) {
const thisTimelogTrElement = await thisTimelogPreElement.evaluateHandle(node => node.closest('tr'));
const thisTimelogTrHtmlContent = await thisTimelogTrElement.innerHTML();
console.log(thisTimelogTrHtmlContent);
}

if (check_duplicate)
await expect(page.getByText(timelog)).toBeHidden();
} else {
else
await expect(page.getByText(timelog)).toBeVisible();
}
}

static async editTimelog(page: Page, timelog: string, taskName: string, taskID: string, date: DateTime, start_time: string, end_time: string)
Expand Down
7 changes: 3 additions & 4 deletions system/modules/tokens/models/ApiOutputService.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ public function apiReturnJsonResponse($response)
{
$this->w->setLayout(null);
http_response_code($response['status']);
// mark header for return content type JSON
if (substr($response['status'], 0, 1) == "2") {
header('Content-Type: application/json');
} else {
header('Content-Type: application/json');

if (substr($response['status'], 0, 1) != "2") {
LogService::getInstance($this->w)->info("API request rejected: " . $response['referer']);
// Don't need, already have set response code!
// header($_SERVER["SERVER_PROTOCOL"] . " " . $response['status'] . " " . $response['payload'][0]);
Expand Down
9 changes: 8 additions & 1 deletion system/templates/base/src/js/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// src/app.ts
import { AlertAdaptation, DropdownAdaptation, FavouritesAdaptation, TabAdaptation, TableAdaptation } from './adaptations';
import { QuillEditor, InputWithOther, MultiFileUpload, MultiSelect, Overlay, CodeMirror } from './components';
import { CodeMirror, InputWithOther, MultiFileUpload, MultiSelect, Overlay, QuillEditor } from './components';

import { Modal, Toast, Tooltip } from 'bootstrap';

Expand Down Expand Up @@ -97,6 +97,13 @@ class Cmfive {
return response.text()
}).then((content) => {
modalContent.innerHTML = content + modalContent.innerHTML;

// https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations
// Appending scripts to the DOM via innerHTML is not meant to execute them for security purposes
// Unfortunately, various modals however contian script tags we need to execute
modalContent.querySelectorAll("script").forEach(x => {
eval(x.innerHTML);
})

// Rebind elements for modal
Cmfive.ready(modalContent);
Expand Down
4 changes: 4 additions & 0 deletions system/templates/base/src/scss/cmfive/_forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ form {
width: 100%;
}
}

input.input-large {
width: 100%;
}
}
Loading

0 comments on commit ee74617

Please sign in to comment.