• 0

[PHP] Issues Displaying a League Table


Question

Hi all,

Please see the following documentation for reference: https://secure.rugby-league.com/api/docs/

A developer provided me with the following PHP file (although I have removed the API key and Secret):

/**
 * League Championships
 */

function fetch_league_table() {
    // Replace with your actual API credentials
    $apiKey = 'xxxx';
    $secret = 'xxxx';

    // Generate a digital signature (sig) using the current timestamp
    $timestamp = time();
    $sig = hash_hmac('sha256', $timestamp, $apiKey . $secret);

    // API endpoint and query parameters
    $apiUrl = 'https://secure.rugby-league.com/api/leaguetable';
    $queryParams = [
        'compID' => 4, // Replace with the appropriate competition ID
        'sig' => $sig
    ];

    // Build the full URL
    $urlWithParams = $apiUrl . '?' . http_build_query($queryParams);

    // Initialize cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $urlWithParams);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Accept: application/json',
        'x-api-key: ' . $apiKey,
        'sig: ' . $sig
    ]);

    // Execute the API request
    $response = curl_exec($ch);

    // Check for errors
    if (curl_errno($ch)) {
        return 'Error fetching data: ' . curl_error($ch);
    }

    curl_close($ch);

    // Decode JSON response
    $data = json_decode($response, true);

        // Handle response and build HTML output
    if ($data['status'] !== 200) {
        return 'No data found.';
    }

    $leagueTable = $data['data']['table'];

    $output = "<h2>" . $data['data']['competition'] . " - " . $data['data']['season'] . "</h2>";
    $output .= "<table border='1' cellpadding='5' cellspacing='0'><thead><tr>";
    $output .= "<th>Position</th><th>Team</th><th>Played</th><th>Wins</th><th>Losses</th><th>Draws</th><th>Points</th></tr></thead><tbody>";

    foreach ($leagueTable as $position => $team) {
        if ($position === 'deductions') continue;
        $output .= "<tr>";
        $output .= "<td>" . $position . "</td>";

        // Fetch the logo URL from the API response
        $logoUrl = isset($team['logo']) ? $team['logo'] : '';
        $logoHtml = $logoUrl ? "<img src='$logoUrl' alt='{$team['teamName']} logo' style='height:20px; margin-right:10px;'>" : '';

        $output .= "<td><strong>" . $logoHtml . $team['teamName'] . "</strong></td>";
        $output .= "<td>" . $team['P'] . "</td>";
        $output .= "<td>" . $team['W'] . "</td>";
        $output .= "<td>" . $team['L'] . "</td>";
        $output .= "<td>" . $team['D'] . "</td>";
        $output .= "<td>" . $team['PTS'] . "</td>";
        $output .= "</tr>";
    }

    $output .= "</tbody></table>";

    // Display deductions if available
    if (isset($leagueTable['deductions'])) {
        $output .= "<h3>Deductions</h3><ul>";
        foreach ($leagueTable['deductions'] as $deduction) {
            $output .= "<li>" . $deduction['team'] . ": -" . $deduction['pts'] . " points (" . $deduction['reason'] . ")</li>";
        }
        $output .= "</ul>";
    }

    return $output;
}

add_shortcode('league_table', 'fetch_league_table');

This PHP file provides us with the table on the website, although I note that the year is 2019. I think this is because the Leaguetable API endpoint doesn't have a year integer. I have been told that I should be able to get the year integer from the Competitions endpoint. But I am not a PHP developer, and I think that the developer that gave me this code got it by going through an AI prompt (i.e. he doesn't know PHP either).

I can see the API endpoint in the code, so I would imagine that I copy that, paste it again but modify the URL and the queryparams to search for year. But what I don't know is how I would then put the query params together so that the table being displayed (compID 4) is for the current year (ex. 2024).

Would someone be able to assist me or point me in the right direction? I tried emailing the support email in the documentation but they have been less than helpful.

Many thanks in advance.

8 answers to this question

Recommended Posts

  • 0

Without the API key and secret I'm not able to test anything, but according to the docs there is no "compID" parameter for the "leaguetable" endpoint, it should be "divisionID", so you're probably getting some kind of default value from 2019  (e.g. since divisionID is omitted it might be using 0)

  • 0
On 11/12/2024 at 22:34, virtorio said:

Without the API key and secret I'm not able to test anything, but according to the docs there is no "compID" parameter for the "leaguetable" endpoint, it should be "divisionID", so you're probably getting some kind of default value from 2019.

Oh damn...that seems right. That would explain why it defaults...

I've received this bit of information since:

Quote

That league table call looks good, expect you only need to pass the division ID to the league table endpoint. See the docs.

To find the divisionIDs and seasonIDs you need to call the competitions endpoint using the relevant compIDs, which are:

Championship: 3
League One: 4
Challenge Cup: 2
1895 Cup: 115
Academy: 52

So the CompID is correct (4).

But now I don't know how to call the competitions endpoint as part of the code from the original post...like I said I'm no developer, especially with PHP. Do I try what I suggested at the start, copy-paste and modify the code for the competition part? But then if so, how do I concatenate the results from the first search and the second?

  • 0

I've updated your code (well I used ChatGPT to update the code as 1. I haven't touched PHP since 2008 and I'm more than a little rusty, and 2. I'm not set up to run any PHP on this computer). You can now re-use the requestApi() function by passing the endpoint url and query params (use of it is shown in the fetch_league_table function).

/**
 * Reusable API request function
 */
function requestApi($endpoint, $queryParams = []) {
    // Replace with your actual API credentials
    $apiKey = 'xxxx';
    $secret = 'xxxx';

    // Generate a digital signature (sig) using the current timestamp
    $timestamp = time();
    $sig = hash_hmac('sha256', $timestamp, $apiKey . $secret);

    // Add the signature to the query parameters
    $queryParams['sig'] = $sig;

    // Build the full URL
    $urlWithParams = $endpoint . '?' . http_build_query($queryParams);

    // Initialize cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $urlWithParams);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Accept: application/json',
        'x-api-key: ' . $apiKey,
        'sig: ' . $sig
    ]);

    // Execute the API request
    $response = curl_exec($ch);

    // Handle cURL errors
    if (curl_errno($ch)) {
        $error = curl_error($ch);
        curl_close($ch);
        return [
            'success' => false,
            'error' => "Error fetching data: $error"
        ];
    }

    curl_close($ch);

    // Decode JSON response
    $data = json_decode($response, true);

    // Check for decoding errors
    if (json_last_error() !== JSON_ERROR_NONE) {
        return [
            'success' => false,
            'error' => 'Error decoding JSON response.'
        ];
    }

    return [
        'success' => true,
        'data' => $data
    ];
}

/**
 * League Championships
 */
function fetch_league_table() {
    $endpoint = 'https://secure.rugby-league.com/api/leaguetable';
    $queryParams = [
        'compID' => 4 // Replace with the appropriate competition ID
    ];

    // Call the reusable request function
    $result = requestApi($endpoint, $queryParams);

    if (!$result['success']) {
        return $result['error'];
    }

    $data = $result['data'];

    // Handle response and build HTML output
    if ($data['status'] !== 200) {
        return 'No data found.';
    }

    $leagueTable = $data['data']['table'];

    $output = "<h2>" . $data['data']['competition'] . " - " . $data['data']['season'] . "</h2>";
    $output .= "<table border='1' cellpadding='5' cellspacing='0'><thead><tr>";
    $output .= "<th>Position</th><th>Team</th><th>Played</th><th>Wins</th><th>Losses</th><th>Draws</th><th>Points</th></tr></thead><tbody>";

    foreach ($leagueTable as $position => $team) {
        if ($position === 'deductions') continue;
        $output .= "<tr>";
        $output .= "<td>" . $position . "</td>";

        // Fetch the logo URL from the API response
        $logoUrl = isset($team['logo']) ? $team['logo'] : '';
        $logoHtml = $logoUrl ? "<img src='$logoUrl' alt='{$team['teamName']} logo' style='height:20px; margin-right:10px;'>" : '';

        $output .= "<td><strong>" . $logoHtml . $team['teamName'] . "</strong></td>";
        $output .= "<td>" . $team['P'] . "</td>";
        $output .= "<td>" . $team['W'] . "</td>";
        $output .= "<td>" . $team['L'] . "</td>";
        $output .= "<td>" . $team['D'] . "</td>";
        $output .= "<td>" . $team['PTS'] . "</td>";
        $output .= "</tr>";
    }

    $output .= "</tbody></table>";

    // Display deductions if available
    if (isset($leagueTable['deductions'])) {
        $output .= "<h3>Deductions</h3><ul>";
        foreach ($leagueTable['deductions'] as $deduction) {
            $output .= "<li>" . $deduction['team'] . ": -" . $deduction['pts'] . " points (" . $deduction['reason'] . ")</li>";
        }
        $output .= "</ul>";
    }

    return $output;
}

add_shortcode('league_table', 'fetch_league_table');

 

For the next step, how would you like it to work? Should it use the "competitions" endpoint to get a list of all "divisionID"'s for a particular (current) year, and then print all those? Is there just a particular divisionID you want displayed, etc?

 

  • 0
On 11/12/2024 at 23:31, virtorio said:

I've updated your code (well I used ChatGPT to update the code as 1. I haven't touched PHP since 2008 and I'm more than a little rusty, and 2. I'm not set up to run any PHP on this computer). You can now re-use the requestApi() function by passing the endpoint url and query params (use of it is shown in the fetch_league_table function).

For the next step, how would you like it to work? Should it use the "competitions" endpoint to get a list of all "divisionID"'s for a particular (current) year, and then print all those? Is there just a particular divisionID you want displayed, etc?

Oh wow! Ok, that's great news! I've just performed a test and it still provides the table for 2019, but as you mentioned it's going to because it doesn't have the year parameter set.

I think you're right about the next step: display all DivisionID's for the current year. I haven't been provided with a specific DivisonID so I guess it would be all of them (at least for the moment)...

Wow, many thanks for taking a shot at this for me. I've been staring at the code for so long trying to understand it my eyes are getting crossed. :laugh:

  • 0

This is what I've got so far, but haven't been able to test it. You're welcome to PM your api key/secret (I won't share it with anyone - promise) if you want and I can run through it.

 

/**
 * Reusable API request function
 */
function request_api($endpoint, $queryParams = []) {
    // Replace with your actual API credentials
    $apiKey = 'xxxx';
    $secret = 'xxxx';

    // Generate a digital signature (sig) using the current timestamp
    $timestamp = time();
    $sig = hash_hmac('sha256', $timestamp, $apiKey . $secret);

    // Add the signature to the query parameters
    $queryParams['sig'] = $sig;

    // Build the full URL
    $urlWithParams = $endpoint . '?' . http_build_query($queryParams);

    // Initialize cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $urlWithParams);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Accept: application/json',
        'x-api-key: ' . $apiKey,
        'sig: ' . $sig
    ]);

    // Execute the API request
    $response = curl_exec($ch);

    // Handle cURL errors
    if (curl_errno($ch)) {
        $error = curl_error($ch);
        curl_close($ch);
        return [
            'success' => false,
            'error' => "Error fetching data: $error"
        ];
    }

    curl_close($ch);

    // Decode JSON response
    $data = json_decode($response, true);

    // Check for decoding errors
    if (json_last_error() !== JSON_ERROR_NONE) {
        return [
            'success' => false,
            'error' => 'Error decoding JSON response.'
        ];
    }

    return [
        'success' => true,
        'data' => $data
    ];
}

/**
 * Renders the league table as HTML
 */
function render_league_table_html($data) {
    // Handle response and build HTML output
    if ($data['status'] !== 200) {
        return 'No data found.';
    }

    $leagueTable = $data['data']['table'];

    $output = "<h2>" . $data['data']['competition'] . " - " . $data['data']['season'] . "</h2>";
    $output .= "<table border='1' cellpadding='5' cellspacing='0'><thead><tr>";
    $output .= "<th>Position</th><th>Team</th><th>Played</th><th>Wins</th><th>Losses</th><th>Draws</th><th>Points</th></tr></thead><tbody>";

    foreach ($leagueTable as $position => $team) {
        if ($position === 'deductions') continue;
        $output .= "<tr>";
        $output .= "<td>" . $position . "</td>";

        // Fetch the logo URL from the API response
        $logoUrl = isset($team['logo']) ? $team['logo'] : '';
        $logoHtml = $logoUrl ? "<img src='$logoUrl' alt='{$team['teamName']} logo' style='height:20px; margin-right:10px;'>" : '';

        $output .= "<td><strong>" . $logoHtml . $team['teamName'] . "</strong></td>";
        $output .= "<td>" . $team['P'] . "</td>";
        $output .= "<td>" . $team['W'] . "</td>";
        $output .= "<td>" . $team['L'] . "</td>";
        $output .= "<td>" . $team['D'] . "</td>";
        $output .= "<td>" . $team['PTS'] . "</td>";
        $output .= "</tr>";
    }

    $output .= "</tbody></table>";

    // Display deductions if available
    if (isset($leagueTable['deductions'])) {
        $output .= "<h3>Deductions</h3><ul>";
        foreach ($leagueTable['deductions'] as $deduction) {
            $output .= "<li>" . $deduction['team'] . ": -" . $deduction['pts'] . " points (" . $deduction['reason'] . ")</li>";
        }
        $output .= "</ul>";
    }

    return $output;
}

/**
 * Fetch competitions based on compIDs
 */
function fetch_competitions($compIDs) {
    $endpoint = 'https://secure.rugby-league.com/api/competitions';
    $queryParams = [
        'compIDs' => implode(',', $compIDs) // Convert array to comma-separated string
    ];

    // Call the reusable request function
    $result = request_api($endpoint, $queryParams);

    if (!$result['success']) {
        return [
            'success' => false,
            'error' => $result['error']
        ];
    }

    $data = $result['data'];

    // Validate response format
    if (!isset($data) || !is_array($data)) {
        return [
            'success' => false,
            'error' => 'Invalid response format from API.'
        ];
    }

    // Map API response to required format
    $competitions = [];
    foreach ($data as $comp) {
        $competition = [
            'compID' => $comp['compID'],
            'compName' => $comp['name'],
            'divisions' => []
        ];

        if (isset($comp['seasons']) && is_array($comp['seasons'])) {
            foreach ($comp['seasons'] as $season) {
                if (isset($season['divisions']) && is_array($season['divisions'])) {
                    foreach ($season['divisions'] as $divisionID => $division) {
                        $competition['divisions'][] = [
                            'divisionID' => $division['divisionID'],
                            'divisionName' => $division['divisionName']
                        ];
                    }
                }
            }
        }

        $competitions[] = $competition;
    }

    return [
        'success' => true,
        'data' => $competitions
    ];
}

/**
 * League Championships
 */
function fetch_league_table() {
    // Always fetch competitions for compID 4
    $compIDs = [4];
    $competitionsResult = fetch_competitions($compIDs);

    if (!$competitionsResult['success']) {
        return $competitionsResult['error'];
    }

    $competitions = $competitionsResult['data'];
    $output = '';

    // Loop through each competition and fetch league table for its divisions
    foreach ($competitions as $competition) {
        foreach ($competition['divisions'] as $division) {
            $divisionID = $division['divisionID'];
            $endpoint = 'https://secure.rugby-league.com/api/leaguetable';
            $queryParams = [
                'divisionID' => $divisionID // Use divisionID instead of compID
            ];

            // Call the reusable request function
            $result = request_api($endpoint, $queryParams);

            if (!$result['success']) {
                $output .= "<p>Error fetching league table for Division ID $divisionID: {$result['error']}</p>";
                continue;
            }

            $data = $result['data'];

            // Pass data to the render function and append the HTML
            $output .= render_league_table_html($data);
        }
    }

    return $output;
}

/**
 * Shortcode to display the league table
 */
add_shortcode('league_table', 'fetch_league_table');

 

 

  • 0

@virtorioI meant to send you the keys earlier, I know I can trust you with them. My bad.

I'll send they keys in a moment, and I'll try the script on the server on my side once I read through it to try and see what is going on. (Y)

  • 0

Seems like it was mostly correct, just needed to correct one array error and it wasn't handling invalid requests from the API properly.

/**
 * Reusable API request function
 */
function request_api($endpoint, $queryParams = []) {
    // Replace with your actual API credentials
    $apiKey = 'xxx';
    $secret = 'xxx';

    // Generate a digital signature (sig) using the current timestamp
    $timestamp = time();
    $sig = hash_hmac('sha256', $timestamp, $apiKey . $secret);

    // Add the signature to the query parameters
    $queryParams['sig'] = $sig;

    // Build the full URL
    $urlWithParams = $endpoint . '?' . http_build_query($queryParams);

    // Initialize cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $urlWithParams);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Accept: application/json',
        'x-api-key: ' . $apiKey,
        'sig: ' . $sig
    ]);

    // Execute the API request
    $response = curl_exec($ch);

    // Handle cURL errors
    if (curl_errno($ch)) {
        $error = curl_error($ch);
        curl_close($ch);
        return [
            'success' => false,
            'error' => "Error fetching data: $error"
        ];
    }

    curl_close($ch);

    // Decode JSON response
    $data = json_decode($response, true);

    // Check for decoding errors
    if (json_last_error() !== JSON_ERROR_NONE) {
        return [
            'success' => false,
            'error' => 'Error decoding JSON response.'
        ];
    }

    if ($data['status'] !== 200) {
        return [
            'success' => false,
            'error' => $data['response']
        ];
    }

    return [
        'success' => true,
        'data' => $data
    ];
}

/**
 * Renders the league table as HTML
 */
function render_league_table_html($data) {
    // Handle response and build HTML output
    if ($data['status'] !== 200) {
        return 'No data found.';
    }

    $leagueTable = $data['data']['table'];

    $output = "<h2>" . $data['data']['competition'] . " - " . $data['data']['season'] . "</h2>";
    $output .= "<table border='1' cellpadding='5' cellspacing='0'><thead><tr>";
    $output .= "<th>Position</th><th>Team</th><th>Played</th><th>Wins</th><th>Losses</th><th>Draws</th><th>Points</th></tr></thead><tbody>";

    foreach ($leagueTable as $position => $team) {
        if ($position === 'deductions') continue;
        $output .= "<tr>";
        $output .= "<td>" . $position . "</td>";

        // Fetch the logo URL from the API response
        $logoUrl = isset($team['logo']) ? $team['logo'] : '';
        $logoHtml = $logoUrl ? "<img src='$logoUrl' alt='{$team['teamName']} logo' style='height:20px; margin-right:10px;'>" : '';

        $output .= "<td><strong>" . $logoHtml . $team['teamName'] . "</strong></td>";
        $output .= "<td>" . $team['P'] . "</td>";
        $output .= "<td>" . $team['W'] . "</td>";
        $output .= "<td>" . $team['L'] . "</td>";
        $output .= "<td>" . $team['D'] . "</td>";
        $output .= "<td>" . $team['PTS'] . "</td>";
        $output .= "</tr>";
    }

    $output .= "</tbody></table>";

    // Display deductions if available
    if (isset($leagueTable['deductions'])) {
        $output .= "<h3>Deductions</h3><ul>";
        foreach ($leagueTable['deductions'] as $deduction) {
            $output .= "<li>" . $deduction['team'] . ": -" . $deduction['pts'] . " points (" . $deduction['reason'] . ")</li>";
        }
        $output .= "</ul>";
    }

    return $output;
}

/**
 * Fetch competitions based on compIDs
 */
function fetch_competitions($compIDs) {
    $endpoint = 'https://secure.rugby-league.com/api/competitions';
    $queryParams = [
        'compIDs' => implode(',', $compIDs) // Convert array to comma-separated string
    ];

    // Call the reusable request function
    $result = request_api($endpoint, $queryParams);

    if (!$result['success']) {
        return [
            'success' => false,
            'error' => $result['error']
        ];
    }

    $data = $result['data'];

    // Validate response format
    if (!isset($data) || !is_array($data)) {
        return [
            'success' => false,
            'error' => 'Invalid response format from API.'
        ];
    }

    // Map API response to required format
    $competitions = [];
    foreach ($data['data'] as $comp) {
        $competition = [
            'compID' => $comp['compID'],
            'compName' => $comp['name'],
            'divisions' => []
        ];

        if (isset($comp['seasons']) && is_array($comp['seasons'])) {
            foreach ($comp['seasons'] as $season) {
                if (isset($season['divisions']) && is_array($season['divisions'])) {
                    foreach ($season['divisions'] as $divisionID => $division) {
                        $competition['divisions'][] = [
                            'divisionID' => $division['divisionID'],
                            'divisionName' => $division['divisionName']
                        ];
                    }
                }
            }
        }

        $competitions[] = $competition;
    }

    return [
        'success' => true,
        'data' => $competitions
    ];
}

/**
 * League Championships
 */
function fetch_league_table() {
    // Always fetch competitions for compID 4
    $compIDs = [4];
    $competitionsResult = fetch_competitions($compIDs);

    if (!$competitionsResult['success']) {
        return $competitionsResult['error'];
    }

    $competitions = $competitionsResult['data'];
    $output = '';

    // Loop through each competition and fetch league table for its divisions
    foreach ($competitions as $competition) {
        foreach ($competition['divisions'] as $division) {
            $divisionID = $division['divisionID'];
            $endpoint = 'https://secure.rugby-league.com/api/leaguetable';
            $queryParams = [
                'divisionID' => $divisionID // Use divisionID instead of compID
            ];

            // Call the reusable request function
            $result = request_api($endpoint, $queryParams);

            if (!$result['success']) {
                $output .= "<p>Error fetching league table for Division ID $divisionID: {$result['error']}</p>";
                continue;
            }

            $data = $result['data'];

            // Pass data to the render function and append the HTML
            $output .= render_league_table_html($data);
        }
    }

    return $output;
}

/**
 * Shortcode to display the league table
 */
add_shortcode('league_table', 'fetch_league_table');

 

  • 0

i was actually reading through this just to try and understand how api stuff like this works but honestly yeah it’s confusing esp with that compID/year thing… did you try calling the competitions endpoint directly? i’m curious what it returns, maybe it lists the compID for each year? like if 2024 has a diff compID than 2019? not sure though lol just guessing tbh. if anyone figures it out pls update here 🙏 kinda want to learn from this too

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Posts

    • A shame it don't allow people to bypass the MS account, I will stick to using Rufus.
    • Microsoft about to radically change how often your Edge browser updates by Paul Hill Microsoft has just announced that starting with Edge 152, it will be moving to a two-week release cycle for faster, smaller updates. This faster release cadence will begin on August 27. This change comes just several months after Microsoft switched Visual Studio Code to weekly updates. The company said that the Extended Stable releases will remain on an eight-week cycle and that no admin changes are needed to experience the faster release cycle on the Stable channel. The new two-week release cycle will enable the faster delivery of security updates and platform improvements, all while reducing the size and complexity of individual updates. Microsoft claims that organizations will benefit from this change as it offers predictable validation cycles. For organizations that prefer a “more deliberate pace”, the Extended Stable channel remains an option. This change will affect Edge Stable releases on Windows, macOS, Linux, and mobile. The Extended Stable channel will continue to be updated every eight weeks, or every fourth Stable release, for example: versions 152, 156, 160, and 164. The Extended Stable could be a good option for organizations that don’t want the latest updates twice a month and don’t want as much hassle constantly updating browsers. In the case of Visual Studio Code, many of the updates being pushed by Microsoft are AI-related. As we all know, Microsoft Edge has a lot of AI features, so we could see Microsoft pushing more AI, thanks to the faster cycles. On the flip side, quicker releases could mean faster security updates, which is beneficial in a world where AI systems are hunting for software exploits. What do you think? Let us know in the comments. For more updates on Edge, be sure to follow Neowin's coverage. In May alone, we reported on Edge offering in-browser pop-ups to assist users with website compatibility issues, that Edge was losing Copilot Mode, and that Microsoft had fixed a plain-text password bug in Edge. Source: Microsoft 365 Admin Center
    • not yet, because at the moment it is not a threat to MS, if and I mean if it did become a threat to MS Office, then it may be a different thing. MS don't like competition
    • Zen Browser 1.21b by Razvan Serea Zen Browser is a privacy-focused, open-source web browser built on Mozilla Firefox, offering users a secure and customizable browsing experience. It emphasizes privacy by blocking trackers, ads, and ensuring your data isn't collected. With Zen Mods, users can enhance their browser experience with various customization options, including features like split views and vertical tabs. The browser is designed for efficiency, providing fast browsing speeds and a lightweight interface. Zen Browser prioritizes user control over the browsing experience, offering a minimal yet powerful alternative to traditional web browsers while keeping your online activity private. Zen Browser’s DRM limitation Zen Browser currently lacks support for DRM-protected content, meaning streaming services like Netflix and HBO Max are inaccessible. This is due to the absence of a Widevine license, which requires significant costs and is financially unfeasible for the developer. Additionally, applying for this license would require Zen to be part of a larger company, similar to Mozilla or Brave. Therefore, DRM-protected media won't be supported in Zen Browser for the foreseeable future. Zen Browser offers features that improve user experience, privacy, and customization: Privacy-Focused: Blocks trackers and minimizes data collection. Automatic Updates: Keeps the browser updated with security patches. Zen Mods: Customizable themes and layouts. Workspaces: Organize tabs into different workspaces. Compact Mode: Maximizes screen space by minimizing UI elements. Zen Glance: Quick website previews. Split Views: View multiple tabs in the same window. Sidebar: Access bookmarks and tools quickly. Vertical Tabs: Manage tabs vertically. Container Tabs: Separate browsing sessions. Fast Profile Switcher: Switch between profiles easily. Tab Folders: Organize tabs into folders. Customizable UI: Personalize browser interface. Security Features: Inherits Firefox’s robust security. Fast Performance: Lightweight and optimized for speed. Zen Mods Customization: Deep customization with mods. Quick Access: Easy access to favorite websites. Open Source: Built on Mozilla Firefox with community collaboration. Community-Driven: Active development and feedback from users. GitHub Repository: Contribute and review the source code. Zen Browser 1.21b changelog: New Features Updated to Firefox 151.0.4 Added 'Space Routing', a new feature that allows you to route links to a specific Space. To get started, click on the three dots in your Space name and click on 'Space Routing Settings'. Fixes Fixed extension-opened tabs not being created with the correct container (#14100) Fixed a bug with the Boosts editor having the wrong theme colors (#14099) Improved overflowing space icons being inaccessible and improved the feeling when hovering over them (#13747) Other minor bug fixes and improvements Download: Zen Browser | 89.8 MB (Open Source) Download: Zen Browser ARM64 | Other Operating Systems View: Zen Browser Home Page | Screenshots 1 | 2 | Reddit Get alerted to all of our Software updates on Twitter at @NeowinSoftware
    • I am currently using a 4tb stick in combination with a 4tb 9100 Pro. It's only been six months since I bought them but the 990 has already dropped 1% health. Fingers crossed it doesn't go any lower.
  • Recent Achievements

    • Week One Done
      davidbazooked earned a badge
      Week One Done
    • One Month Later
      Jamswaz earned a badge
      One Month Later
    • Week One Done
      Jamswaz earned a badge
      Week One Done
    • Rookie
      Marzoid went up a rank
      Rookie
    • Community Regular
      coch went up a rank
      Community Regular
  • Popular Contributors

    1. 1
      +primortal
      514
    2. 2
      PsYcHoKiLLa
      185
    3. 3
      +Edouard
      159
    4. 4
      Steven P.
      83
    5. 5
      ATLien_0
      75
  • Tell a friend

    Love Neowin? Tell a friend!