Azure Communication Service add participants not working

I was trying to add a phone number to a call using Azure Communication Service but it kept failing

const incoming_context = event.data.incomingCallContext;
                    const answer_res = await client.answerCall(incoming_context, `${config.domain}`);

                    await answer_res.callConnection.addParticipant([{
                        targetParticipant: { phoneNumber: '+xxxxxxxxxxx' },
                        sourceDisplayName: "Info",
                        sourceCallerIdNumber: { phoneNumber: '+xxxxxxxxxxx' }
                    }]);
Received 1 event(s)
Error answering call: TypeError: Cannot read properties of undefined (reading 'communicationUserId')
at isCommunicationUserIdentifier (C:UsersrohitDesktopgitcall-recordnode_modules@azurecommunication-commondistindex.js:326:30)
at getIdentifierKind (C:UsersrohitDesktopgitcall-recordnode_modules@azurecommunication-commondistindex.js:366:9)
at serializeCommunicationIdentifier (C:UsersrohitDesktopgitcall-recordnode_modules@azurecommunication-commondistindex.js:518:28)
at communicationIdentifierModelConverter (C:UsersrohitDesktopgitcall-recordnode_modules@azurecommunication-call-automationdistcommonjsutliconverters.js:95:94)
at CallConnection.addParticipant (C:UsersrohitDesktopgitcall-recordnode_modules@azurecommunication-call-automationdistcommonjscallConnection.js:130:89)
at C:UsersrohitDesktopgitcall-recordapp.js:34:53
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)

Is there a way to select and deselect an entire group of choices on a pickerInput from shinyWidgets in 2025?

I am aware there is a similar question that was answered pre 2023, but the internal structure of pickerInput has changed since. – Old Fix

I will also use the same reproducible example used in that question:

library(shiny)
library(shinyWidgets)

ui <- fluidPage(
    pickerInput("test",choices=list("A"=c(1,2,3,4,5),"B"=c(6,7,8,9,10)),multiple=TRUE),
    textOutput("testOutput")
)

server <- function(input, output) {
    output$testOutput <- renderText({paste(input$test)})
}

shinyApp(ui = ui, server = server)

The goal is to click A and have the pickerInput automatically select 1,2,3,4 and 5 or/and if we click B, it automatically selects 6,7,8,9, and 10.

I also want the reverse, so clicking A and B when they are selected deselects 1,2,3,4 and 5 and 6,7,8,9, and 10 respectively.

Desired ouput after clicking “A”

SVG will translate but won’t rotate

This rect translates fine but won’t rotate but I can’t see the typo or syntax error.
[Firefox 138.0(64bit) on openSUSE Tumbleweed]

    <script  id="svgScript" type="text/javascript">
        //https://github.com/amitonline/true-ruler/blob/main/js/true-ruler.js
        var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttribute("id","sliderControl");
        svg.setAttribute("viewBox","0 0 100% 100%");
        svg.setAttribute("width","100%");
        svg.setAttribute("height","200px");
        
        var svgNS = svg.namespaceURI;


        const periodPill = document.createElementNS(svgNS, "rect");
        periodPill.setAttribute("x", "0");
        periodPill.setAttribute("y", "0");
        periodPill.setAttribute("width", "30");
        periodPill.setAttribute("height", "30");
        periodPill.setAttribute("transform-origin", "center");
        periodPill.setAttribute("transform", "rotate(45)");
        periodPill.setAttribute("transform", "translate(532,85)");
        periodPill.setAttribute("fill", "#55aaff");
        periodPill.setAttribute("opacity","100%");
        periodPill.setAttribute("cursor", "move");
        svg.appendChild(periodPill);

        document.body.appendChild(svg);
    </script>

Inject/execute JavaScript from Python script

I’m executing a method in my Python script that triggers a function in my HTML file:

def OnLoadingStateChange(self, browser, is_loading, **_):
    if not is_loading:
        self.container.page_loaded = True

        blueprint_text = """
            Begin Object Class=/Script/BlueprintGraph.K2Node_Event Name="K2Node_Event_1" ExportPath="/Script/BlueprintGraph.K2Node_Event'/Game/ANCIENT_DRAGON/ABP_AncientDragon.ABP_AncientDragon:EventGraph.K2Node_Event_1'"
            NodePosX=3392
            NodePosY=-64
            NodeGuid=EC7283AA406DE84613834BA97BBD68F1
            EventReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.AnimInstance'",MemberName="AnimNotify_FootShake")
            CustomFunctionName="AnimNotify_FootShake"
            CustomProperties Pin (PinId=318791834E3D459539EC3B87A24112FB,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/Engine.AnimBlueprintGeneratedClass'/Game/ANCIENT_DRAGON/ABP_AncientDragon.ABP_AncientDragon_C'",MemberName="AnimNotify_FootShake",MemberGuid=EC7283AA406DE84613834BA97BBD68F1),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False)
            End Object
        """

        js_code = f'renderBlueprint("{blueprint_text}");'
        browser.ExecuteJavascript(js_code)

When I exclude the below line, it works fine but when included it gives an error stating that the node cannot be rendered.

CustomProperties Pin (PinId=318791834E3D459539EC3B87A24112FB,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/Engine.AnimBlueprintGeneratedClass'/Game/ANCIENT_DRAGON/ABP_AncientDragon.ABP_AncientDragon_C'",MemberName="AnimNotify_FootShake",MemberGuid=EC7283AA406DE84613834BA97BBD68F1),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False)

I’ve tried including this within the function to help the formatting but it makes no difference:

escaped_text = (
    blueprint_text
    .replace('\', '\\')
    .replace('n', '\n')
    .replace('"', '\"')
)

This is the function in my HTML file:

function renderBlueprint(blueprintText) {
// Stop any existing renderer
if (renderer) {
    renderer.stop();
    playground.innerHTML = ''; // Clear the playground
}
// Create and start a new renderer
renderer = new window.blueprintUE.render.Main(
    blueprintText,
    playground,
    { height: "100vh" }
);
renderer.start();

I’ve uploaded the JS file here.

Can anyone see why the “CustomProperties Pin” line would break its functionality?

Using a css and js autocomplete api or something in a VSCode extension

I created a new code language that accepts css and js codes. Just like html supports css and js, my new language also supports css and js codes. Everything is working fine, but the problem is that I can’t make the css and js codes auto-extract and colored, so I’m trying to do it manually, which can lead to more errors. Is there a special service that can help me do this? I found the following service, but it only works in the browser. I need it to be used in an extension. I would appreciate any help.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>CSS Auto Completion with Monaco Editor</title>
  <style>
    #editor {
      width: 100%;
      height: 400px;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>

<div id="editor"></div>

<script src="https://unpkg.com/monaco-editor@latest/min/vs/loader.js"></script>
<script>
  require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@latest/min/vs' } });

  require(['vs/editor/editor.main'], function () {
    monaco.editor.create(document.getElementById('editor'), {
      value: ``,
      language: "css",
      theme: "vs-dark",
      fontSize: 14,
      automaticLayout: true
    });
  });
</script>

</body>
</html>

I tried several options like the example above, but none of them seem to work within the extension.

File Picker and Upload Functionality Not Working in Moodle LMS

I created a course and tried to add a SCORM file using the File Picker. However, when I click the ‘Add’ button, nothing happens—the popup window for file upload does not appear. I also attempted to upload PNG and JPG files instead of the SCORM package, but the issue persists. The file upload functionality is not working on the Moodle LMS site.

I can see error in DOM (screen attached).
enter image description here

Spring Boot font files return 404 or 302 only when browser DevTools are open (localhost)

I’m working on a Spring Boot application with the following setup:

Spring Boot version: 2.4.1

Java version: 1.8

Using spring-boot-starter-security and spring-boot-starter-web

Application context path is set to /admin via application.yml: server:   servlet:     context-path: /admin Font files are located in: src/main/resources/static/FKGroteskNeue-1.1.1/woff-static/ and woff2-static/

CSS references fonts using absolute paths like:

@font-face {   font-family: "FK Grotesk Neue";   src: url('/admin/FKGroteskNeue-1.1.1/woff2-static/FKGroteskNeue-Regular.woff2') format('woff2'),        url('/admin/FKGroteskNeue-1.1.1/woff-static/FKGroteskNeue-Regular.woff') format('woff'); }

When I access the app via http://localhost:8085/admin/…, everything works until I open browser DevTools.

As soon as I open DevTools (Inspect Element), font requests like:

http://localhost:8085/FKGroteskNeue-1.1.1/woff2-static/FKGroteskNeue-Regular.woff2
return either:

404 Not Found, or
302 Found → redirecting to /admin/login
When I access the app using my local IP address (e.g., http://192.168.x.x:8085), the fonts load fine — even with DevTools open.

I Verified font files exist in target/classes/static/…
Updated pom.xml to exclude font files from filtering
Cleared browser cache and tried Incognito mode.
Updated Spring Security config to permit font paths:
.antMatchers(“/admin/FKGroteskNeue-1.1.1/**”).permitAll()

When switching between “films” and “serials”, previous content is not replaced and incorrect cards are shown

Problem, need to clean up the network

•   When I open the /films page, a request is made to films.json and movie cards are displayed.
•   As I scroll down, more films are loaded in batches of 20 — works fine so far.
•   Then I navigate to /serials, a request to serials.json is made and serial cards are shown — also works fine.
•   But when I go back to /films, a new request is correctly made to films.json, but the serial cards from the previous view are still on the page, instead of being replaced with films.

So the path changes and the new request is triggered, but the old cards (from /serials) remain.

Expected behavior: When switching routes, the current cards should be cleared, and only the new content relevant to the route should be shown.

What I’ve tried:

  • Clearing sessionstorage

Any ideas on what might be going wrong?

export default function MoviesList({
                                       mainpage,
                                       referer,
                                       list = [],
                                       filterData,
                                       catalogData,
                                       searchParams,
                                       priorityArray,
                                       query = undefined,
                                       setNewData = null,
                                       sessionStorageFilms,
                                       sessionStorageSerials,
                                       maximumAgeLevel,
                                       isHistoryCard
                                   }) {
    const [data, setData] = useState(list);
    const [reset, setReset] = useState(false);
    const [asPath, setAsPath] = useState(searchParams?.categoryAlias);
    const skipNumber =
        asPath === «films»
            ? sessionStorageFilms?.length
            : asPath === «serials»
                ? sessionStorageSerials?.length
                : +process.env.LIMIT_SCROLL_ITEMS_PER_PAGE;

    if (asPath !== searchParams?.categoryAlias) {
        // when the page changes, a command to reset skip is sent
        setAsPath(searchParams.categoryAlias);
    }

    const { result, loading } = infiniteScrollMovieList(
        mainpage,
        catalogData,
        searchParams,
        filterData,
        reset,
        query,
        skipNumber, //to load the number of subsequent films stored in sessionStorage
        maximumAgeLevel,
        sessionStorageFilms,
        sessionStorageSerials
    );

    useEffect(() => {
        if (
            result?.items?.length &&
            data?.length &&
            result.items[result?.items?.length - 1].name !== data[data.length - 1].name
        ) {
            const newData = [...data, ...result?.items];

            if (
                setNewData === null &&
                sessionStorageSerials?.[20]?.name !== result?.items[0].name &&
                sessionStorageFilms?.[20]?.name !== result?.items[0].name
            ) {
                setData(newData);

                if (asPath === «films») {
                    sessionStorage.setItem(«films», JSON.stringify(newData));
                } else if (asPath === «serials») {
                    sessionStorage.setItem(«serials», JSON.stringify(newData));
                }
            } else if (
                setNewData !== null &&
                !Boolean(
                    data.find(item => item.name === result.items[result?.items?.length - 1].name)
                )
            ) {
                setNewData(newData);
            }
        }
    }, [result, sessionStorageSerials, sessionStorageFilms]);

    useEffect(() => {
        // retrieve films from sessionStorage, if they are there
        if (asPath === «films») {
            list.length > 0 && setData(sessionStorageFilms?.length > 0 ? sessionStorageFilms : list);
        } else if (asPath === «serials») {
            list.length > 0 && setData(sessionStorageSerials?.length > 0 ? sessionStorageSerials : list);
        } else {
            setData(list);
        }

        if (setNewData === null) {
            setReset(true);
            setTimeout(() => {
                setReset(false);
            }, 1000);
        }
    }, [asPath, filterData, list, sessionStorageSerials, sessionStorageFilms, setNewData]);

thanks

with a function to map a point onto a quadrilateral, can you make a quadrilateral in which 3 points maintain their relative position inside it? [closed]

I program in JavaScript, and a recent problem I’ve been facing is related to a feature I implemented that interpolates a point between a quadrilateral like so

This is a rough depiction of point interpolation

It works perfectly fine, however another feature I need to make is the ability to get the opposite.

Is it possible to create an algorithm that can take an input of 3 points between 0 and 1 and output the corner coordinates of a quadrilateral that matches? Here’s a diagram to help explain it if it helps. The points on the quadrilateral match the points in the corner

If so, a guide on how to implement it would be great.

I’ve been basically brute-forcing this idea for a while to no avail. I just might simply be not smart enough to tackle the problem, I can get one point into place but not two or three at the same time.

Formatting output of the Web Speech API

record = new SpeechRecognition();
record.onresult = (speech) => { alert(speech.results[0][0].transcript); }
record.start();

First letter of above code`s output is upper case. How can it come from server as lower case?

For example, output text is: Here we go.

However, demanded text is: here we go.

toLowerCase method is not accepted in this thread.

In my react app, browser refresh/reload redirects me to home page

I am using Reactjs for building the app. My Route page, index.js and App page looks like below. When I open the app, it lands in a home page, I select 1 of the radio buttons, submit it. It navigates to the next page. Now if I click on browser refresh/reload button, it redirects me to home page instead of reloading same page which is the default behaviour.

Routes Page

`const Routes = () => {
  const element = (
    <ReactRoutes>
      <Route path="/" element={<MainView />} />
      <Route path="/Home" element={<Home />} />
      <Route exact path='/login' element={<Login />} />
      <Route path="/Management" element={<ManagementTable />} />
      <Route path="/Overview" element={<Overview />} />
    </ReactRoutes>
  );
  return element;
};
export default Routes;`

App.jsx

    const App = () => {
  const [isAuthState,setIsAuthState] = useState(false);
  const { authState } = useOktaAuth();
  useEffect(() => {
    if(authState && authState !== null) {
      setIsAuthState(true);
    }
  }, [authState]);

  if (isAuthState && authState?.isAuthenticated) {
    return (
      <>
          <Header />
          <MainNav />
          <Routes />
          <GlobalFooter />
      </>
    );
  } else {
    return (
      <CustomRoutes>
        <Route exact path='/login' element={<Login />} />
        <Route exact path='/login/callback' element={<LoginCallback />} />
        <Route path='*' element={<Login />} />
      </CustomRoutes>
    );
  }
};
export default OktaWrapper(App);

OktWrapper.jsx

    function OktaWrapper(WrapperComponent) {
    const oktaAuth = new OktaAuth(oktaAuthConfig);
    function HOC() {
        const navigate = useNavigate();
        const restoreOriginalUri = () => {
            navigate('/');
        };

        const customAuthHandler = () => {
            navigate('/login');
        };
        return (
            <Security
                oktaAuth={oktaAuth}
                restoreOriginalUri={restoreOriginalUri}
                onAuthRequired={customAuthHandler}
            >
                <WrapperComponent oktaAuth={oktaAuth} />
            </Security>
        );
    }
    return HOC;
}

export default OktaWrapper;

index.js

    const root = document.getElementById('root');
ReactDOM.render(
  <React.StrictMode>
     <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  root
);
reportWebVitals();

LCP is 2.6s even after optimizing dynamic image (22KB size)

I’m trying to improve the Largest Contentful Paint (LCP) on one of my pages. The main LCP element is an image that is loaded dynamically via PHP.

I have already compressed the image and the final size is just 22KB, but the LCP is still reported as 2.6 seconds in PageSpeed Insights.

Here is the relevant CSS:

.ArtistImageD001 {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 0 20px 20px 0;
}

And here’s the image element in my PHP/HTML:

<img alt="<?php echo $artistDetail->first_name . ' ' . $artistDetail->last_name; ?>"
     src="<?php echo base_url(); ?>apppanel/assets/artistimage/<?php echo $imageUrlFinalShow; ?>"
     class="ArtistImageD001" />

Despite the small image size and optimizations, LCP is still high.

What I’ve tried so far:

  • Compressed the image to 22KB.
  • Made sure object-fit: cover doesn’t cause content shifts.
  • Verified that the image is not lazy-loaded.

Questions I have:

  1. What else can I do to improve LCP in this case?
  2. Is there any PHP or HTML-related optimization I can make since the
    image is dynamic?
  3. Should I use <link rel="preload"> or something else for the image?

LCP Image

LCP Image

Is there a way to specify the database using the LDAP php extension to connect to an Active Directory?

Let’s say I have a server called examplesite.database.windows.net that has a database called Database. The username and password are [email protected] and password respectively.

I need to connect via “Active Directory – Password” authentication as per my company’s policy (I do not have privileges to change anything about the server, switching to alternative forms of authentication are also disallowed).

I would like to use PHP to connect to database and am currently trying out the LDAP php extension, but I could not find anything within the LDAP documentation that would allow me to specify the database property (“Database” in this example) to connect to. The account in question only has privileges for this database and not the entire server.

Can anyone point me in the right direction?

If LDAP does not enable the Database property to be specified, are there any alternatives within php that are suitable for Active Directory connections?

Some sample code I tried is below:

<?php 
$ldap_dn = "uid=account,dc=example,dc=com";
$ldap_password = "password";
$ldap_con = ldap_connect("examplesite.database.windows.net") or die('Could not connect to LDAP server'); 
ldap_set_option($ldap_con, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_con, LDAP_OPT_REFERRALS, 0);

if ($ldap_con)
    $ldap_bind = ldap_bind($ldap_con, $ldap_dn, $ldap_password);

    if ($ldap_bind) {
        echo "Bind Successful";
    } else {
        echo "Error";
}
?> 

GA4 Measurement Protocol purchase event not appearing in DebugView when sent from backend using stored _ga client_id

I’m trying to send a purchase event to Google Analytics 4 using the Measurement Protocol from my Laravel backend. I’m not using gtag.js or Firebase — only server-side requests.

Here’s what I’m doing:

  1. On the frontend, I extract the client_id from the _ga cookie using JavaScript and send it to the backend (e.g., via Ajax or form data).
  2. I store that client_id in the database (associated with the user or session).
  3. Later, when the user completes a purchase (which could be minutes or even days after the initial visit), I use the previously saved client_id to send a purchase event via Measurement Protocol.

Here’s a simplified version of the code I use:

public function gtmPaidOrder() {
    $clientId = $this->client_id; // previously saved from frontend _ga cookie

    if (!$clientId) return false;

    $measurement_id = 'G-XXXXXXX';
    $api_secret = MY_SECRET;

    $payload = [
        'client_id' => $clientId,
        'events' => [
            [
                'name'   => 'purchase',
                'params' => [
                    'transaction_id' => $this->id,
                    'value'          => $this->getTotalPrice(),
                    'currency'       => $this->currency,
                    'items'          => [
                        [
                            'item_id' => '123',
                            'item_name' => 'Test Product',
                            'price' => 20,
                            'quantity' => 1
                        ]
                    ],
                    'debug_mode' => true
                ]
            ]
        ]
    ];

    $url = "https://www.google-analytics.com/debug/mp/collect?measurement_id={$measurement_id}&api_secret={$api_secret}";

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    Log::info('GA4 MP response', ['response' => $response]);
}

Google returns a 200 OK response with the following body:

{
  "validationMessages": [ ]
}

All other events sent from the frontend appear in DebugView, but this event does not. Why?

Custom 404 page – Custom PHP framework

I’m using custom PHP framework (here) and when route not found it’s showing an error page:

Fatal error
Uncaught exception: 'Exception'

Message: 'No route matched.'

Stack trace:

#0 C:xampphtdocsprojectswashpublicindex.php(40): CoreRouter->dispatch('plan/plan/views')
#1 {main}
Thrown in 'C:xampphtdocsprojectswashCoreRouter.php' on line 130

This is very useful while programming but now I’ll go live and I need only 404 page appear if no route found.

I found 404 & 500 html page under App->Views directory but it’s not work!

Below I’ll put the code of index.php, error.php & routre.php files, and you can refer to the link above to check the whole framework structure.

Please note that sometimes I don’t know how to describe my issue so please notify me before you vote my question.

public/index.php:

<?php

/**
 * Front controller
 *
 * PHP version 7.0
 */

/**
 * Composer
 */
require dirname(__DIR__) . '/vendor/autoload.php';


/**
 * Error and Exception handling
 */
error_reporting(E_ALL);
set_error_handler('CoreError::errorHandler');
set_exception_handler('CoreError::exceptionHandler');

/**
 * Sessions
 */
 session_start();


/**
 * Routing
 */
$router = new CoreRouter();

// Add the routes
$router->add('', ['controller' => 'Home', 'action' => 'index']);
$router->add('Login', ['controller' => 'login', 'action' => 'new']);
$router->add('Logout', ['controller' => 'login', 'action' => 'destroy']);
$router->add('password/reset/{token:[da-f]+}', ['controller' => 'Password', 'action' => 'reset']);
$router->add('{controller}/{action}');
    
$router->dispatch($_SERVER['QUERY_STRING']);

Core>Error.php

<?php

namespace Core;

/**
 * Error and exception handler
 *
 * PHP version 7.0
 */
class Error
{

    /**
     * Error handler. Convert all errors to Exceptions by throwing an ErrorException.
     *
     * @param int $level  Error level
     * @param string $message  Error message
     * @param string $file  Filename the error was raised in
     * @param int $line  Line number in the file
     *
     * @return void
     */
    public static function errorHandler($level, $message, $file, $line)
    {
        if (error_reporting() !== 0) {  // to keep the @ operator working
            throw new ErrorException($message, 0, $level, $file, $line);
        }
    }

    /**
     * Exception handler.
     *
     * @param Exception $exception  The exception
     *
     * @return void
     */
    public static function exceptionHandler($exception)
    {
        // Code is 404 (not found) or 500 (general error)
        $code = $exception->getCode();
        if ($code != 404) {
            $code = 500;
        }
        http_response_code($code);

        if (AppConfig::SHOW_ERRORS) {
            echo "<h1>Fatal error</h1>";
            echo "<p>Uncaught exception: '" . get_class($exception) . "'</p>";
            echo "<p>Message: '" . $exception->getMessage() . "'</p>";
            echo "<p>Stack trace:<pre>" . $exception->getTraceAsString() . "</pre></p>";
            echo "<p>Thrown in '" . $exception->getFile() . "' on line " . $exception->getLine() . "</p>";
        } else {
            $log = dirname(__DIR__) . '/logs/' . date('Y-m-d') . '.txt';
            ini_set('error_log', $log);

            $message = "Uncaught exception: '" . get_class($exception) . "'";
            $message .= " with message '" . $exception->getMessage() . "'";
            $message .= "nStack trace: " . $exception->getTraceAsString();
            $message .= "nThrown in '" . $exception->getFile() . "' on line " . $exception->getLine();

            error_log($message);

            View::renderTemplate("$code.html");
        }
    }
}

Core>Router.php

<?php

namespace Core;

/**
 * Router
 *
 * PHP version 7.0
 */
class Router
{

    /**
     * Associative array of routes (the routing table)
     * @var array
     */
    protected $routes = [];

    /**
     * Parameters from the matched route
     * @var array
     */
    protected $params = [];

    /**
     * Add a route to the routing table
     *
     * @param string $route  The route URL
     * @param array  $params Parameters (controller, action, etc.)
     *
     * @return void
     */
    public function add($route, $params = [])
    {
        // Convert the route to a regular expression: escape forward slashes
        $route = preg_replace('///', '\/', $route);

        // Convert variables e.g. {controller}
        $route = preg_replace('/{([a-z]+)}/', '(?P<1>[a-z-]+)', $route);

        // Convert variables with custom regular expressions e.g. {id:d+}
        $route = preg_replace('/{([a-z]+):([^}]+)}/', '(?P<1>2)', $route);

        // Add start and end delimiters, and case insensitive flag
        $route = '/^' . $route . '$/i';

        $this->routes[$route] = $params;
    }

    /**
     * Get all the routes from the routing table
     *
     * @return array
     */
    public function getRoutes()
    {
        return $this->routes;
    }

    /**
     * Match the route to the routes in the routing table, setting the $params
     * property if a route is found.
     *
     * @param string $url The route URL
     *
     * @return boolean  true if a match found, false otherwise
     */
    public function match($url)
    {
        foreach ($this->routes as $route => $params) {
            if (preg_match($route, $url, $matches)) {
                // Get named capture group values
                foreach ($matches as $key => $match) {
                    if (is_string($key)) {
                        $params[$key] = $match;
                    }
                }

                $this->params = $params;
                return true;
            }
        }

        return false;
    }

    /**
     * Get the currently matched parameters
     *
     * @return array
     */
    public function getParams()
    {
        return $this->params;
    }

    /**
     * Dispatch the route, creating the controller object and running the
     * action method
     *
     * @param string $url The route URL
     *
     * @return void
     */
    public function dispatch($url)
    {
        $url = $this->removeQueryStringVariables($url);

        if ($this->match($url)) {
            $controller = $this->params['controller'];
            $controller = $this->convertToStudlyCaps($controller);
            $controller = $this->getNamespace() . $controller;

            if (class_exists($controller)) {
                $controller_object = new $controller($this->params);

                $action = $this->params['action'];
                $action = $this->convertToCamelCase($action);

                if (preg_match('/action$/i', $action) == 0) {
                    $controller_object->$action();

                } else {
                    throw new Exception("Method $action in controller $controller cannot be called directly - remove the Action suffix to call this method");
                }
            } else {
                throw new Exception("Controller class $controller not found");
            }
        } else {
            throw new Exception('No route matched.', 404);
        }
    }

    /**
     * Convert the string with hyphens to StudlyCaps,
     * e.g. post-authors => PostAuthors
     *
     * @param string $string The string to convert
     *
     * @return string
     */
    protected function convertToStudlyCaps($string)
    {
        return str_replace(' ', '', ucwords(str_replace('-', ' ', $string)));
    }

    /**
     * Convert the string with hyphens to camelCase,
     * e.g. add-new => addNew
     *
     * @param string $string The string to convert
     *
     * @return string
     */
    protected function convertToCamelCase($string)
    {
        return lcfirst($this->convertToStudlyCaps($string));
    }

    /**
     * Remove the query string variables from the URL (if any). As the full
     * query string is used for the route, any variables at the end will need
     * to be removed before the route is matched to the routing table. For
     * example:
     *
     *   URL                           $_SERVER['QUERY_STRING']  Route
     *   -------------------------------------------------------------------
     *   localhost                     ''                        ''
     *   localhost/?                   ''                        ''
     *   localhost/?page=1             page=1                    ''
     *   localhost/posts?page=1        posts&page=1              posts
     *   localhost/posts/index         posts/index               posts/index
     *   localhost/posts/index?page=1  posts/index&page=1        posts/index
     *
     * A URL of the format localhost/?page (one variable name, no value) won't
     * work however. (NB. The .htaccess file converts the first ? to a & when
     * it's passed through to the $_SERVER variable).
     *
     * @param string $url The full URL
     *
     * @return string The URL with the query string variables removed
     */
    protected function removeQueryStringVariables($url)
    {
        if ($url != '') {
            $parts = explode('&', $url, 2);

            if (strpos($parts[0], '=') === false) {
                $url = $parts[0];
            } else {
                $url = '';
            }
        }

        return $url;
    }

    /**
     * Get the namespace for the controller class. The namespace defined in the
     * route parameters is added if present.
     *
     * @return string The request URL
     */
    protected function getNamespace()
    {
        $namespace = 'AppControllers\';

        if (array_key_exists('namespace', $this->params)) {
            $namespace .= $this->params['namespace'] . '\';
        }

        return $namespace;
    }
}