<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Anjanesh]]></title><description><![CDATA[Web App Developer · Web UI Performance · Cloud newbie · Navi Mumbai · MentorCap]]></description><link>https://anjanesh.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1714828166724/a7a9eb6c-59e4-4bae-868c-2f27eecac995.jpeg</url><title>Anjanesh</title><link>https://anjanesh.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 10 May 2026 02:46:55 GMT</lastBuildDate><atom:link href="https://anjanesh.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Email validity checker using Google's JSON API for DNS over HTTPS (DoH) in Google Sheets]]></title><description><![CDATA[AutoSend seems to he a promising utility belt for sending out (legit) mass emails be it for a campaign or transactional.
There was a post today by the creator of AutoSend - https://www.linkedin.com/fe]]></description><link>https://anjanesh.dev/email-validity-checker-using-google-s-json-api-for-dns-over-https-doh-in-google-sheets</link><guid isPermaLink="true">https://anjanesh.dev/email-validity-checker-using-google-s-json-api-for-dns-over-https-doh-in-google-sheets</guid><category><![CDATA[google sheets]]></category><category><![CDATA[email]]></category><category><![CDATA[google-dns]]></category><category><![CDATA[MX Record]]></category><category><![CDATA[autosend]]></category><category><![CDATA[mass email campaigns]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Tue, 21 Apr 2026 17:37:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/20f7e790-86dd-4a19-aff3-bad58ffcc5bf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AutoSend seems to he a promising utility belt for sending out (legit) mass emails be it for a campaign or transactional.</p>
<p>There was a post today by the creator of AutoSend - <a href="https://www.linkedin.com/feed/update/urn:li:activity:7452373365497085952/">https://www.linkedin.com/feed/update/urn:li:activity:7452373365497085952/</a> - and I remembered about a Google Sheet tool I wrote in Google App Script that would validate email ids. Not so robust but enough to weed out bad email ids.</p>
<p>Open a new Google Sheet and goto <strong>Extensions &gt; Apps Script</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/e8b35222-b60f-4068-ab9e-ae21bca4629b.png" alt="" style="display:block;margin:0 auto" />

<p>Delete</p>
<pre><code class="language-javascript">function myFunction() {
  
}
</code></pre>
<p>and paste this instead :</p>
<pre><code class="language-javascript">function onOpen()
{
  main();
}

function main()
{
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var ui = SpreadsheetApp.getUi();

  ui.createMenu('Domains')
      .addItem('Email Cleaner', 'EmailChecker_OneByOne')      
      .addToUi();
    
  Logger.log("init");

}

function EmailChecker_OneByOne()
{  
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getActiveSheet();  
  var firstrow = 2;
  var range = sheet.getRange(firstrow, 1, sheet.getLastRow() - firstrow + 1, 1);
  var data = range.getValues();

  sheet.getRange('B1').setValue("Valid Email").setHorizontalAlignment("center").setFontWeight('bold');  
  sheet.getRange('C1').setValue("Status").setHorizontalAlignment("center").setFontWeight('bold');
  sheet.getRange('D1').setValue("Answers").setHorizontalAlignment("center").setFontWeight('bold');
  sheet.getRange('E1').setValue("Is Disposable").setHorizontalAlignment("center").setFontWeight('bold');


  for (var i = 0; i &lt; data.length; i++)
  {
    if (data[i] == "") continue;
    
    var domain = String(data[i]).split('@').pop();
    var response = JSON.parse(UrlFetchApp.fetch("https://dns.google/resolve?name=" + domain + "&amp;type=MX"));

    if (!response.error)
    {      
      if (!response.Answer) answers_length = 0; else answers_length = response.Answer.length;
    }

    disposable = isDisposable(data[i]);

    if (answers_length &gt; 0)
    {
      sheet.getRange('B' + (i + 2)).setValue(data[i]);
    }

    sheet.getRange('C' + (i + 2)).setValue(response.Status);
    sheet.getRange('D' + (i + 2)).setValue(answers_length);
    sheet.getRange('E' + (i + 2)).setValue(disposable);
    
    SpreadsheetApp.flush();

    Logger.log(response);
  }  
}

function isDisposable(email)
{
  // https://open.kickbox.com/v1/disposable/mail%40anjanesh.biz
  var url = "https://open.kickbox.com/v1/disposable/" + encodeURI(email);
  var response = JSON.parse(UrlFetchApp.fetch(url));
  return response.disposable;
}
</code></pre>
<p>And save to Drive.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/4de830e6-1a31-45f9-9701-b54a1f1dc4a9.png" alt="" style="display:block;margin:0 auto" />

<p>Now, go back to the spreadsheet and reload the page. (F5 or CMD + R). You should see a new menu item called Domains next to Help. Don't click on it yet.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/759332a3-045c-4254-afeb-deab58e38a22.png" alt="" style="display:block;margin:0 auto" />

<p>Now Enter Email in A1 and paste a list of email ids you want validated in column A. Now click on <strong>Domains &gt; Email Cleaner</strong>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/ceb2fa04-755c-4333-89c4-23f3c75e068e.png" alt="" style="display:block;margin:0 auto" />

<p>You'll get a popup saying "Authorisation required" - "A script attached to this document needs your permission to run." - Click on Ok.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/aa12ef2e-bc30-459a-b9f5-c1c3e2cf2ad8.png" alt="" style="display:block;margin:0 auto" />

<p>You'll get one popup - "Google hasn’t verified this app" - click on Advanced.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/336b47a4-7477-4e12-bbd8-eb686dc9c39c.png" alt="" style="display:block;margin:0 auto" />

<p>Continue only if you understand the risks and trust the developer (<a href="mailto:yourgmail@gmail.com">yourgmail@gmail.com</a>). Click on <strong>Go to Untitled project (unsafe)</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/fcacc3c4-b9d1-4afd-9637-86aa5f3d002e.png" alt="" style="display:block;margin:0 auto" />

<p>Google will ask for access. Grant it access.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e5e8afd205a551d167f94ef/344d3b6e-54c7-4f58-ba0b-db070e94bbcf.png" alt="" style="display:block;margin:0 auto" />

<p>You should see columns B, C, D and E populated.</p>
<p><a class="embed-card" href="https://youtu.be/hcWmCsruG1E">https://youtu.be/hcWmCsruG1E</a></p>

<p>The connect to external service is to connect to Google's JSON API for DNS over HTTPS (DoH) - <a href="https://developers.google.com/speed/public-dns/docs/doh/json">https://developers.google.com/speed/public-dns/docs/doh/json</a></p>
<p>For example an email like <a href="mailto:johnsmith@yahoo.in">johnsmith@yahoo.in</a> we cross-check MX records with Google's public DNS checker :</p>
<p><a href="https://dns.google/resolve?name=johnsmith@yahoo.in&amp;type=MX">https://dns.google/resolve?name=johnsmith@yahoo.in&amp;type=MX</a></p>
<p>Many mass mailing solutions will suspend your account if the bounce rate is very high - and this happens even when the collection is legit - like when you have email ids written down on paper material at in-person events.</p>
<p>What would be really cool if AutoSend could be used to send out emails within Google Spreadsheets right <a href="https://hashnode.com/@yogini" class="user-mention" data-type="mention" title="Yogini Bende">Yogini Bende</a>?</p>
]]></content:encoded></item><item><title><![CDATA[Getting picoclaw to work on Windows]]></title><description><![CDATA[My friend Shabeer Naha WhatsApped me this link today - https://youtube.com/shorts/HUueAGGKy9I?si=kJ-h7G6Y3sW0ql9h - and asked "Is this true?"
This was my first time I was hearing of picoclaw and I was]]></description><link>https://anjanesh.dev/picoclaw</link><guid isPermaLink="true">https://anjanesh.dev/picoclaw</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Thu, 19 Feb 2026 12:45:26 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/5e5e8afd205a551d167f94ef/21135b1a-89bf-4a8a-a9b7-b936961adb66.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My friend Shabeer Naha WhatsApped me this link today - <a href="https://youtube.com/shorts/HUueAGGKy9I?si=kJ-h7G6Y3sW0ql9h">https://youtube.com/shorts/HUueAGGKy9I?si=kJ-h7G6Y3sW0ql9h</a> - and asked "Is this true?"</p>
<p>This was my first time I was hearing of picoclaw and I wasn't perturbed. Everyday something in the AI world is coming up and I can no longer keep track of these. I wanted to try openclaw but refrained when I read about agents performing actions automatically on your behalf. And people are reportedly buying multiple Mac Minis for this !?! But what I saw in the video about picoclaw was that it can run even on 10MB of RAM because it doesn't really run the agent on your local device. I mean ... the agent <em>is</em> on your local device but the LLM isn't.</p>
<p>So I decided to give this a shot on my Windows laptop - the expendable laptop.</p>
<p>The docs on the homepage (<a href="https://picoclaw.net/#install">https://picoclaw.net/#install</a>) says just this :</p>
<pre><code class="language-shell">git clone https://github.com/sipeed/picoclaw.git
cd picoclaw &amp;&amp; picoclaw onboard
picoclaw agent
# Or send a single message:
picoclaw agent -m "Hello, introduce yourself"
</code></pre>
<p>This is very misleading. Because you can't just cd into a just cloned directory and run a binary esp. if it doesn't exist. The directory in this GitHub has no executable - <a href="https://github.com/sipeed/picoclaw">https://github.com/sipeed/picoclaw</a></p>
<p>First of all, you need to run this in a Linux environment so I used WSL2 on Windows which was already loaded with Ubuntu 22.04.</p>
<pre><code class="language-shell">git clone https://github.com/sipeed/picoclaw.git
</code></pre>
<p>The folder seems to be <strong>go</strong> project so the project had to be built with the <strong>go</strong> compiler. Check for your <strong>go</strong> version - it requires almost the latest version - mine was very much outdated since I don't code in <strong>go</strong> yet.</p>
<pre><code class="language-shell">go version
go version go1.18.1 linux/amd64
</code></pre>
<p>Update go.</p>
<pre><code class="language-plaintext">sudo apt remove golang-go -y &amp;&amp; sudo apt autoremove -y
sudo rm -rf /usr/local/go
wget https://go.dev/dl/go1.25.7.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.25.7.linux-amd64.tar.gz
vi ~/.bashrc
</code></pre>
<p>Now add this line to the very bottom of <code>~/.bashrc</code></p>
<pre><code class="language-shell">export PATH=$PATH:/usr/local/go/bin
</code></pre>
<p>Then execute these :</p>
<pre><code class="language-shell">source ~/.bashrc
go version
rm go1.25.7.linux-amd64.tar.gz
make deps
make build
cd build
./picoclaw onboard
</code></pre>
<p>You should see something like this on your WSL2 terminal :</p>
<pre><code class="language-shell">anjanesh@ZEROBOOKULTRA:~/picoclaw/build$ ./picoclaw onboard
🦞 picoclaw is ready!

Next steps:
  1. Add your API key to /home/anjanesh/.picoclaw/config.json
     Get one at: https://openrouter.ai/keys
  2. Chat: picoclaw agent -m "Hello!"
</code></pre>
<p>I signed up at <a href="http://openrouter.ai">openrouter.ai</a> and got myself an API key.</p>
<pre><code class="language-shell">vi /home/anjanesh/.picoclaw/config.json
</code></pre>
<p>Just edit it to enter the <a href="http://openrouter.ai">openrouter.ai</a> key</p>
<pre><code class="language-shell">"openrouter": {
  "api_key": "enter-your-openrouter-API-key-here",
  "api_base": ""
}
</code></pre>
<p>Also, you need to edit the default provider and model.</p>
<pre><code class="language-plaintext">  "agents": {
    "defaults": {
      "workspace": "~/.picoclaw/workspace",
      "restrict_to_workspace": true,
      "provider": "openrouter",
      "model": "openai/gpt-5.2",
      "max_tokens": 8192,
      "temperature": 0.7,
      "max_tool_iterations": 20
    }
  },
</code></pre>
<p>Now if you enter a prompt like this :</p>
<pre><code class="language-shell">./picoclaw agent -m "What does the company Growthink do ?"
</code></pre>
<p>After a whole lot of info lines being printed for about 1-2 minutes, I get the answer as :</p>
<pre><code class="language-plaintext">2026/02/19 18:00:54 [2026-02-19T12:30:54Z] [INFO] tool: Tool execution completed {tool=web_fetch, duration_ms=435, result_length=93}
2026/02/19 18:01:00 [2026-02-19T12:31:00Z] [INFO] agent: LLM response without tool calls (direct answer) {content_chars=748, agent_id=main, iteration=12}
2026/02/19 18:01:01 [2026-02-19T12:31:01Z] [INFO] agent: Response: Growthink is a business consulting firm (founded in 1999) that helps entrepreneurs and companies grow and raise capit... {agent_id=main, session_key=agent:main:main, iterations=12, final_length=748}

🦞 Growthink is a business consulting firm (founded in 1999) that helps entrepreneurs and companies grow and raise capital. In practice, they provide:

- **Business planning** (business plan writing/consulting and related strategy work)
- **Market research** (industry/market/competitive research to support decisions and fundraising)
- **Investment banking / capital raising support** (helping companies prepare investor materials, refine the investor story/financials, and run parts of the fundraising process)

Sources: Growthink “Services” page (Business Planning, Market Research, Investment Banking) https://www.growthink.com/services and Growthink “About” pages https://www.growthink.com/about-us / https://www.growthink.com/about/.
</code></pre>
<p>Why does a CLI still take so much time that doesn't require compute power on your device (and I'm on a 200 Mbps line) is beyond me.</p>
<p>PS: I loaded $10 credits to my <a href="http://openrouter.ai">openrouter.ai</a> account to try other models. Pricing (<a href="https://openrouter.ai/pricing">https://openrouter.ai/pricing</a>) says 25+ free models and 4 free providers on the free tier.</p>
]]></content:encoded></item><item><title><![CDATA[The core use-case of TypeScript]]></title><description><![CDATA[I am going through this course on NextJS (mainly for the sake of Supabase) - and for my project setup, I choose TypeScript instead of plain JavaScript even though the Udemy course uses JavaScript only.
There is a local end-point we have that spits a ...]]></description><link>https://anjanesh.dev/the-core-use-case-of-typescript</link><guid isPermaLink="true">https://anjanesh.dev/the-core-use-case-of-typescript</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[json]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Wed, 21 Jan 2026 11:22:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768994534236/7c6e316d-fc3d-4719-a72e-ae59a938de5a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am going through <a target="_blank" href="https://www.udemy.com/course/master-nextjs-full-stack/">this course on NextJS</a> (mainly for the sake of <a target="_blank" href="https://supabase.com">Supabase</a>) - and for my project setup, I choose TypeScript instead of plain JavaScript even though the Udemy course uses JavaScript only.</p>
<p>There is a local end-point we have that spits a bit of JSON data</p>
<p><a target="_blank" href="http://localhost:3100/transactions">http://localhost:3100/transactions</a></p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"1"</span>,
    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">100</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"Expense"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Restaurant"</span>,
    <span class="hljs-attr">"category"</span>: <span class="hljs-string">"Food"</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-03-15T23:00:00"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"2"</span>,
    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">159</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"Expense"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Gas"</span>,
    <span class="hljs-attr">"category"</span>: <span class="hljs-string">"Car"</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-03-15T23:00:00"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"3"</span>,
    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">500</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"Income"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Salary"</span>,
    <span class="hljs-attr">"category"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-03-15T23:00:00"</span>
  }
]
</code></pre>
<p>When we do something like this :</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'http://localhost:3100/transactions'</span>);
<span class="hljs-keyword">const</span> transactions = <span class="hljs-keyword">await</span> response.json();
</code></pre>
<p>We don't know (in the code) what kind of data we're getting before-hand unless we inspect the JSON response. But if we have the blueprint ready and really know what are the possible response values back, we can define this hard-coded in as TypeScript <em>types</em> which is the very core of TypeScript.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> TransactionType = <span class="hljs-string">'Income'</span> | <span class="hljs-string">'Expense'</span> | <span class="hljs-string">'Saving'</span> | <span class="hljs-string">'Investment'</span>;

<span class="hljs-keyword">interface</span> Transaction
{
    id: <span class="hljs-built_in">number</span>;
    <span class="hljs-keyword">type</span>: TransactionType,
    category?: <span class="hljs-built_in">string</span>,
    description: <span class="hljs-built_in">string</span>,
    amount: <span class="hljs-built_in">number</span>,
}
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'http://localhost:3100/transactions'</span>);
<span class="hljs-keyword">const</span> transactions: Transaction[] = <span class="hljs-keyword">await</span> response.json();
<span class="hljs-keyword">if</span> (!<span class="hljs-built_in">Array</span>.isArray(transactions)) <span class="hljs-keyword">return</span> &lt;p&gt;No transactions found.&lt;/p&gt;;

<span class="hljs-keyword">return</span> (&lt;section className=<span class="hljs-string">"space-y-4"</span>&gt;
    {transactions.map(<span class="hljs-function"><span class="hljs-params">transaction</span> =&gt;</span> &lt;div key={transaction.id}&gt;
        &lt;TransactionItem
            <span class="hljs-keyword">type</span>={transaction.type}
            category={transaction.category}
            description={transaction.description}
            amount={transaction.amount}
        /&gt;
    &lt;/div&gt;)}
&lt;/section&gt;)
</code></pre>
<p>Here we “tell” the TypeScript engine that const transactions would be of type <code>Transaction</code> and also it would be an <strong>array</strong> (of <code>Transaction</code>s).</p>
<p>PS: Here <strong>TransactionItem</strong> is a custom component that does a whole lot of HTML with DIVs, formatting and styling.</p>
]]></content:encoded></item><item><title><![CDATA[Hashnode's GraphQL API for RSS feeds]]></title><description><![CDATA[All this while, parsing my blog's RSS feed from https://anjanesh.dev/rss.xml on my server was possible to extract the contents of title, description, link, image and date to be inserted at my NextJS powered template at https://anjanesh.com/blogs.
But...]]></description><link>https://anjanesh.dev/hashnode-graphql</link><guid isPermaLink="true">https://anjanesh.dev/hashnode-graphql</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[rss]]></category><category><![CDATA[REST API]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Wed, 21 Jan 2026 10:06:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768989901923/ab20e32b-d427-458c-ab8b-c663163353a4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>All this while, parsing my blog's RSS feed from <a target="_blank" href="https://anjanesh.dev/rss.xml">https://anjanesh.dev/rss.xml</a> on my server was possible to extract the contents of title, description, link, image and date to be inserted at my NextJS powered template at <a target="_blank" href="https://anjanesh.com/blogs">https://anjanesh.com/blogs</a>.</p>
<p>But Hashnode recently made some changes to their blogging system where the front-end seem to sit on a Vercel.</p>
<pre><code class="lang-bash">php blog.rss.php
PHP Warning:  file_get_contents(https://anjanesh.dev/rss.xml): failed to open stream: HTTP request failed! HTTP/1.0 429 Too Many Requests
</code></pre>
<p>It looks we've hit a Vercel rate limit, as per <a target="_blank" href="https://docs.vercel.com/docs/rest-api/reference/welcome#rate-limits">https://docs.vercel.com/docs/rest-api/reference/welcome#rate-limits</a>. But I got it working using their GraphQL API though - <a target="_blank" href="https://docs.hashnode.com/quickstart/introduction">https://docs.hashnode.com/quickstart/introduction</a></p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetch_hashnode_posts</span>(<span class="hljs-params">$domain</span>)
</span>{
    $url = <span class="hljs-string">"https://gql.hashnode.com"</span>;

    $query = <span class="hljs-string">'
    query {
      publication(host: "'</span> . $domain . <span class="hljs-string">'") {
        posts(first: 10) {
          edges {
            node {
              title
              brief
              url
              publishedAt
              coverImage {
                url
              }
            }
          }
        }
      }
    }'</span>;

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, <span class="hljs-literal">true</span>);
    curl_setopt($ch, CURLOPT_POST, <span class="hljs-literal">true</span>);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([<span class="hljs-string">'query'</span> =&gt; $query]));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        <span class="hljs-string">'Content-Type: application/json'</span>,
        <span class="hljs-string">'User-Agent: My-Hashnode/2.5'</span>
    ]);

    $response = curl_exec($ch);
    $result = json_decode($response, <span class="hljs-literal">true</span>);
    curl_close($ch);

    <span class="hljs-keyword">return</span> $result;
}
</code></pre>
<p>This has got me interested in knowing more about GraphQL which seem to be the way forward from REST API, esp RSS feeds.</p>
]]></content:encoded></item><item><title><![CDATA[make makemigrations work again]]></title><description><![CDATA[In Django, the recommended standard is to push the migrations folder of (an app) also to git.
Initially I never used to push migration files to GitHub and I ended up having to do python make migrations as well as python manage.py migrate on the serve...]]></description><link>https://anjanesh.dev/make-makemigrations-work-again</link><guid isPermaLink="true">https://anjanesh.dev/make-makemigrations-work-again</guid><category><![CDATA[Django]]></category><category><![CDATA[migrations]]></category><category><![CDATA[Git]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Mon, 17 Mar 2025 12:48:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742215562143/7b31580a-0c0b-44cb-9eb6-0029f6751335.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Django, the recommended standard is to push the <strong>migrations</strong> folder of (an app) also to git.</p>
<p>Initially I never used to push migration files to GitHub and I ended up having to do <code>python make migrations</code> as well as <code>python manage.py migrate</code> on the server - whether it was manually or via a pipeline.</p>
<p>There was one common pitfall to this which I stumbled on many times only to figure it out later - this post is to remind me of how to come out of that pit.</p>
<p><code>python mange.py makemigrations</code> would always say “No changes detected” even when you had a model in a brand new app.</p>
<p><mark>Note:</mark> This is only relevant to new Django ‘apps’ created (or pulled from Git) - if you had already done <strong>makemigrations</strong> and <strong>migrate</strong> on an app previously and it worked then running the same for the second time would work. The reason why it doesn’t work for the first time is because Django looks for a migrations folder and the <code>__init__.py</code> file in the <strong>migrations</strong> folder for <em>makemigrations</em> to work.</p>
<p>Goto to the app, say it be <strong>booking</strong> and create a migrations folder and create an empty <code>__init__.py</code> file in it. Now run <code>python mange.py makemigrations</code>, it should work as expected.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> booking
mkdir migrations
<span class="hljs-built_in">cd</span> migrations
touch __init__.py
<span class="hljs-built_in">cd</span> ../..
python manage.py makemigrations
python manage.py migrate
</code></pre>
]]></content:encoded></item><item><title><![CDATA[How to setup Django with React using InertiaJS]]></title><description><![CDATA[The official Django Inertia adapter was released in December 2022 but there was 0 front-end documentation and only the Django part of documentation - even now (as of 2nd February 2025) it says "Django specific frontend docs coming soon." with referen...]]></description><link>https://anjanesh.dev/how-to-setup-django-with-react-using-inertiajs</link><guid isPermaLink="true">https://anjanesh.dev/how-to-setup-django-with-react-using-inertiajs</guid><category><![CDATA[Django]]></category><category><![CDATA[React]]></category><category><![CDATA[Inertia.js]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Front-end Development]]></category><category><![CDATA[setup]]></category><category><![CDATA[vite]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Sun, 02 Feb 2025 19:05:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738523078616/27997080-39b2-43fa-baf2-a5f0ca840ff2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The official Django Inertia adapter was released in December 2022 but there was 0 front-end documentation and only the Django part of documentation - even now (as of 2nd February 2025) it says "Django specific frontend docs coming soon." with references to 2 other repos from where we need to pull our hair to try to get it to work. I finally landed on <a target="_blank" href="https://github.com/mujahidfa/inertia-django-vite-vue-minimal">Mujahid Anuar's repo</a> which was for Vue but managed to port it to React with bits and pieces from StackOverflow, <a target="_blank" href="http://Claude.ai">Claude.ai</a> and online documentation.</p>
<p>This is not a tutorial on Django or React - this article shows how to bind React in Django using Inertia instead of using API endpoints using DRF. So I am cutting short adding models in Django etc to delve directly into the front-end usage sending hardcoded props to React code.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> workspace/django
mkdir inertia-django-vite-react-minimal
<span class="hljs-built_in">cd</span> inertia-django-vite-react-minimal
python3 -m venv venv
<span class="hljs-built_in">source</span> venv/bin/activate
pip install django==5.1.5
django-admin startproject inertia_django_vite_react_minimal .
pip install django-vite==3.0.6 inertia-django==1.1.0 whitenoise==6.8.2
python manage.py startapp app
</code></pre>
<p>Install Node if you haven't already have - minimum version required is version 22.0.0</p>
<pre><code class="lang-bash">touch package.json
code .
</code></pre>
<p>Add this to <code>package.json</code> :</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
        <span class="hljs-attr">"build"</span>: <span class="hljs-string">"vite build"</span>
    },
    <span class="hljs-attr">"devDependencies"</span>: {
        <span class="hljs-attr">"vite"</span>: <span class="hljs-string">"^6.0.11"</span>
    },
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"@inertiajs/progress"</span>: <span class="hljs-string">"^0.2.7"</span>,
        <span class="hljs-attr">"@inertiajs/react"</span>: <span class="hljs-string">"^2.0.3"</span>,
        <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^22.10.10"</span>,
        <span class="hljs-attr">"@vitejs/plugin-react"</span>: <span class="hljs-string">"^4.3.4"</span>,
        <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^19.0.0"</span>,
        <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^19.0.0"</span>
    }
}
</code></pre>
<pre><code class="lang-bash">npm install
</code></pre>
<p>If you get something like this because you already have an older NodeJS installed, either re-install NodeJS version 22.0 or later or use <a target="_blank" href="https://github.com/nvm-sh/nvm">nvm</a>.</p>
<pre><code class="lang-bash">npm warn EBADENGINE Unsupported engine {
npm warn EBADENGINE   package: <span class="hljs-string">'vite@6.0.11'</span>,
npm warn EBADENGINE   required: { node: <span class="hljs-string">'^18.0.0 || ^20.0.0 || &gt;=22.0.0'</span> },
npm warn EBADENGINE   current: { node: <span class="hljs-string">'v21.7.3'</span>, npm: <span class="hljs-string">'10.9.0'</span> }
npm warn EBADENGINE }
npm warn deprecated lodash.isequal@4.5.0: This package is deprecated. Use require(<span class="hljs-string">'node:util'</span>).isDeepStrictEqual instead.
</code></pre>
<p>I have nvm installed so I had to do <code>nvm install 22</code></p>
<pre><code class="lang-bash">touch vite.config.ts
</code></pre>
<p>Add this to <code>vite.config.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> { resolve } <span class="hljs-keyword">from</span> <span class="hljs-string">"path"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">root</span>: resolve(<span class="hljs-string">"./app/static/src"</span>),
  <span class="hljs-attr">base</span>: <span class="hljs-string">"/static/"</span>,
  <span class="hljs-attr">plugins</span>: [react()],
  <span class="hljs-attr">build</span>: {
    <span class="hljs-attr">outDir</span>: resolve(<span class="hljs-string">"./app/static/dist"</span>),
    <span class="hljs-attr">assetsDir</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">manifest</span>: <span class="hljs-string">"manifest.json"</span>,
    <span class="hljs-attr">emptyOutDir</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">rollupOptions</span>: {
      <span class="hljs-comment">// Overwrite default .html entry to main.tsx in the static directory</span>
      <span class="hljs-attr">input</span>: resolve(<span class="hljs-string">"./app/static/src/main.tsx"</span>),
    },
  },
});
</code></pre>
<pre><code class="lang-bash">python manage.py migrate
python manage.py runserver
<span class="hljs-comment"># Open a new tab in the terminal in the same project directory and run</span>
npm run dev
</code></pre>
<p>In <code>inertia_django_vite_react_minimal/</code><a target="_blank" href="http://urls.py"><code>urls.py</code></a> you should have URLs of <code>app</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> include, path

urlpatterns = [
    path(<span class="hljs-string">'admin/'</span>, admin.site.urls),
    path(<span class="hljs-string">""</span>, include(<span class="hljs-string">"app.urls"</span>)), <span class="hljs-comment"># Include app's URLs </span>
]
</code></pre>
<p>Create a file called <a target="_blank" href="http://urls.py">urls.py</a> in the <code>app</code> folder.</p>
<p>In app/<a target="_blank" href="http://urls.py">urls.py</a> let's create the <code>home</code> and <code>about</code> URLs:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path

<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> views

urlpatterns = [
    path(<span class="hljs-string">""</span>, views.index, name=<span class="hljs-string">"index"</span>),
    path(<span class="hljs-string">"about"</span>, views.about, name=<span class="hljs-string">"about"</span>),
]
</code></pre>
<p>And in the <a target="_blank" href="http://views.py">views.py</a> :</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpRequest
<span class="hljs-keyword">from</span> django.shortcuts <span class="hljs-keyword">import</span> render
<span class="hljs-keyword">from</span> inertia <span class="hljs-keyword">import</span> render <span class="hljs-keyword">as</span> inertia_render <span class="hljs-comment"># So that we can keep using django's default render() for non Inertia/React pages</span>
<span class="hljs-keyword">from</span> time <span class="hljs-keyword">import</span> sleep

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>(<span class="hljs-params">request</span>):</span>
    <span class="hljs-keyword">return</span> inertia_render(request, <span class="hljs-string">"Index"</span>, props={<span class="hljs-string">"name"</span>: <span class="hljs-string">"World"</span>})


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">about</span>(<span class="hljs-params">request</span>):</span>
    sleep(<span class="hljs-number">2.5</span>) <span class="hljs-comment"># This is to show the loading progress indicator on the front-end using the @inertiajs/progress package</span>
    <span class="hljs-keyword">return</span> inertia_render(request, <span class="hljs-string">"About"</span>, props={<span class="hljs-string">"pageName"</span>: <span class="hljs-string">"About"</span>})
</code></pre>
<p>Create a folder called <code>templates</code> in the <code>app</code> folder.</p>
<p>In the <code>templates</code> folder create file called <code>index.html</code> paste this content in it :</p>
<pre><code class="lang-html">{% load django_vite %}
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"csrf-token"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"{{ csrf_token }}"</span>&gt;</span>

    {% if debug %}
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">import</span> RefreshRuntime <span class="hljs-keyword">from</span> <span class="hljs-string">'http://localhost:5173/static/@react-refresh'</span>
    RefreshRuntime.injectIntoGlobalHook(<span class="hljs-built_in">window</span>)
    <span class="hljs-built_in">window</span>.$RefreshReg$ = <span class="hljs-function">() =&gt;</span> {}
    <span class="hljs-built_in">window</span>.$RefreshSig$ = <span class="hljs-function">() =&gt;</span> <span class="hljs-function">(<span class="hljs-params">type</span>) =&gt;</span> type
    <span class="hljs-built_in">window</span>.__vite_plugin_react_preamble_installed__ = <span class="hljs-literal">true</span>
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    {% endif %}

  {% vite_hmr_client %}
  {% vite_asset 'main.tsx' %}

  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Inertia + Django + Vite + React minimal<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  {% block inertia %}{% endblock %}
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>The <a target="_blank" href="http://localhost:5173/static/@react-refresh"><code>http://localhost:5173/static/@react-refresh</code></a> is for hot reload in development mode - when <code>DEBUG</code> is true is in <a target="_blank" href="http://settings.py">settings.py</a> - when you want the front-end to reload automatically on changes in the React files. Reference: <a target="_blank" href="https://stackoverflow.com/a/77971927/126833">https://stackoverflow.com/a/77971927/126833</a></p>
<p>Create a folder called <code>static</code> in the <code>app</code> folder and create a sub-folder called <code>src</code> In <code>src</code> create a file called <code>main.tsx</code> and have this in it :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-string">"vite/modulepreload-polyfill"</span>;
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> { createInertiaApp } <span class="hljs-keyword">from</span> <span class="hljs-string">"@inertiajs/react"</span>;
<span class="hljs-keyword">import</span> { InertiaProgress } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/progress'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> { Page } <span class="hljs-keyword">from</span> <span class="hljs-string">"@inertiajs/core"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;  <span class="hljs-comment">// Added this import</span>
<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">const</span> csrfToken = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'meta[name=csrf-token]'</span>).content;
    axios.defaults.headers.common[<span class="hljs-string">'X-CSRF-Token'</span>] = csrfToken;

    InertiaProgress.init();

    createInertiaApp({
        resolve: <span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">`./pages/<span class="hljs-subst">${name}</span>.tsx`</span>),
        setup({ el, App, props }: {
            el: HTMLElement,
            App: React.ComponentType&lt;{ page: Page }&gt;,
            props: <span class="hljs-built_in">any</span>
        }) {
            <span class="hljs-keyword">const</span> root = createRoot(el);
            root.render(&lt;App {...props} /&gt;);
        },
    });

});
</code></pre>
<p>Create 2 files in the <code>app/static/src/pages</code> folder - one named <code>Index.tsx</code> and the other named <code>About.tsx</code> - these are pure React code in TypeScript.</p>
<p><strong>Index.tsx</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { Link, usePage } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Index</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> { app_name } = usePage().props;

  <span class="hljs-keyword">return</span> (
      &lt;div&gt;
          &lt;h1&gt;{app_name}&lt;/h1&gt;
          &lt;h1&gt;Welcome to the Home Page&lt;/h1&gt;
          &lt;Link href=<span class="hljs-string">"/about"</span>&gt;About&lt;/Link&gt;
      &lt;/div&gt;
  );
}
</code></pre>
<p><strong>About.tsx</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {Link, usePage} <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> { app_name } = usePage().props;

  <span class="hljs-keyword">return</span> (
      &lt;div&gt;
          &lt;h1&gt;{app_name}&lt;/h1&gt;
          &lt;h1&gt;About Page&lt;/h1&gt;
          &lt;Link href=<span class="hljs-string">"/"</span>&gt;Back to Home&lt;/Link&gt;
      &lt;/div&gt;
  );
}
</code></pre>
<p>Create a folder in app called middleware and in middleware create a file called <a target="_blank" href="http://mInertia.py">mInertia.py</a> (I purposely didn't want to name it <a target="_blank" href="http://inertia.py">inertia.py</a> incase of conflicts due to the existing file of the same name in packages)</p>
<p>app/middleware/<a target="_blank" href="http://mInertia.py">mInertia.py</a></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> inertia <span class="hljs-keyword">import</span> share
<span class="hljs-keyword">from</span> django.conf <span class="hljs-keyword">import</span> settings
<span class="hljs-keyword">from</span> django.contrib.auth <span class="hljs-keyword">import</span> get_user_model


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InertiaShareMiddleware</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, get_response</span>):</span>
        self.get_response = get_response

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, request</span>):</span>
        <span class="hljs-comment"># Share data that should be available to all components</span>
        share(
            request,
            app_name=settings.APP_NAME, <span class="hljs-comment"># This is set in settings.py</span>
            user=<span class="hljs-keyword">lambda</span>: self._get_user_data(request),
            user_count=<span class="hljs-keyword">lambda</span>: get_user_model().objects.count(),
            <span class="hljs-comment"># Add more shared data as needed</span>
        )

        <span class="hljs-keyword">return</span> self.get_response(request)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_get_user_data</span>(<span class="hljs-params">self, request</span>):</span>
        <span class="hljs-string">"""Format user data for frontend components"""</span>
        <span class="hljs-keyword">if</span> request.user.is_authenticated:
            <span class="hljs-keyword">return</span> {
                <span class="hljs-string">'id'</span>: request.user.id,
                <span class="hljs-string">'email'</span>: request.user.email,
                <span class="hljs-string">'name'</span>: request.user.get_full_name(),
                <span class="hljs-string">'is_staff'</span>: request.user.is_staff,
            }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<p>Create a file called context_<a target="_blank" href="http://processors.py">processors.py</a> in inertia_django_vite_react_minimal - this is to send the <code>DEBUG</code> value with the keyname <code>debug</code> to the front-end template to use as <code>{% if debug %}</code> ... <code>{% endif %}</code></p>
<p>inertia_django_vite_react_minimal/context_<a target="_blank" href="http://processors.py">processors.py</a> :</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.conf <span class="hljs-keyword">import</span> settings

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">debug_mode</span>(<span class="hljs-params">request</span>):</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">'debug'</span>: settings.DEBUG}
</code></pre>
<p>There a number of edits in <a target="_blank" href="http://settings.py">settings.py</a> (inertia_django_vite_react_minimal/<a target="_blank" href="http://settings.py">settings.py</a>) :</p>
<pre><code class="lang-bash">import os
import re
.
.
.
DEBUG = True
APP_NAME = <span class="hljs-string">"Django with Inertia using React"</span> <span class="hljs-comment"># This was added</span>
ALLOWED_HOSTS = [<span class="hljs-string">"*"</span>]

<span class="hljs-comment"># Application definition</span>

INSTALLED_APPS = [
    <span class="hljs-string">'whitenoise.runserver_nostatic'</span>, <span class="hljs-comment"># This was added</span>
    <span class="hljs-string">'django.contrib.admin'</span>,
    <span class="hljs-string">'django.contrib.auth'</span>,
    <span class="hljs-string">'django.contrib.contenttypes'</span>,
    <span class="hljs-string">'django.contrib.sessions'</span>,
    <span class="hljs-string">'django.contrib.messages'</span>,
    <span class="hljs-string">'django.contrib.staticfiles'</span>,
    <span class="hljs-string">'django_vite'</span>, <span class="hljs-comment"># This was added</span>
    <span class="hljs-string">'inertia'</span>, <span class="hljs-comment"># This was added</span>
    <span class="hljs-string">'app'</span>, <span class="hljs-comment"># This was added</span>
]

MIDDLEWARE = [
    <span class="hljs-string">'django.middleware.security.SecurityMiddleware'</span>,
    <span class="hljs-string">'whitenoise.middleware.WhiteNoiseMiddleware'</span>,
    <span class="hljs-string">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
    <span class="hljs-string">'django.middleware.common.CommonMiddleware'</span>,
    <span class="hljs-string">'django.middleware.csrf.CsrfViewMiddleware'</span>,
    <span class="hljs-string">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,
    <span class="hljs-string">'django.contrib.messages.middleware.MessageMiddleware'</span>,
    <span class="hljs-string">'django.middleware.clickjacking.XFrameOptionsMiddleware'</span>,
    <span class="hljs-string">'inertia.middleware.InertiaMiddleware'</span>, <span class="hljs-comment"># This was added</span>
    <span class="hljs-string">'app.middleware.mInertia.InertiaShareMiddleware'</span>, <span class="hljs-comment"># This was added</span>
]
.
.
.
<span class="hljs-comment"># django-vite settings</span>
<span class="hljs-comment"># https://github.com/MrBin99/django-vite</span>
DJANGO_VITE_DEV_MODE = DEBUG  <span class="hljs-comment"># DEBUG - follow Django's dev mode</span>

<span class="hljs-comment"># Where ViteJS assets are built.</span>
DJANGO_VITE_ASSETS_PATH = BASE_DIR / <span class="hljs-string">"app"</span> / <span class="hljs-string">"static"</span> / <span class="hljs-string">"dist"</span>

<span class="hljs-comment"># Vite 3 defaults to 5173. Default for django-vite is 3000, which is the default for Vite 2.</span>
DJANGO_VITE_DEV_SERVER_PORT = 5173

<span class="hljs-comment"># Output directory for collectstatic to put all your static files into.</span>
STATIC_ROOT = BASE_DIR / <span class="hljs-string">"staticfiles"</span>

DJANGO_VITE_MANIFEST_PATH = os.path.join(STATIC_ROOT, <span class="hljs-string">"manifest.json"</span>)

<span class="hljs-comment"># Include DJANGO_VITE_ASSETS_PATH into STATICFILES_DIRS to be copied inside</span>
<span class="hljs-comment"># when run command python manage.py collectstatic</span>
STATICFILES_DIRS = [DJANGO_VITE_ASSETS_PATH]

<span class="hljs-comment"># Inertia settings</span>
INERTIA_LAYOUT = BASE_DIR / <span class="hljs-string">"app"</span> / <span class="hljs-string">"templates/index.html"</span>

<span class="hljs-comment"># Vite generates files with 8 hash digits</span>
<span class="hljs-comment"># http://whitenoise.evans.io/en/stable/django.html#WHITENOISE_IMMUTABLE_FILE_TEST</span>
def immutable_file_test(path, url):
    <span class="hljs-comment"># Match filename with 12 hex digits before the extension</span>
    <span class="hljs-comment"># e.g. app.db8f2edc0c8a.js</span>
    <span class="hljs-built_in">return</span> re.match(r<span class="hljs-string">"^.+\.[0-9a-f]{8,12}\..+$"</span>, url)
</code></pre>
<p>Now when you goto <a target="_blank" href="http://localhost:8000/">http://localhost:8000/</a> (I’m assuming that <code>python manage.py runserver</code> and <code>npm run dev</code> are already running) you should see this which is served by app/static/src/Pages/Index.tsx :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738521027839/9b73dad1-1905-4009-a0f3-0d8e48d62809.png" alt class="image--center mx-auto" /></p>
<p>And on clicking About, you should be able to see the About Page page loading in 2 and half seconds with a blue progress indicator showing at the top of the page which is processed by <code>@inertiajs/progress</code> which we added in <code>package.json</code>.</p>
<p>GitHub repo : <a target="_blank" href="https://github.com/anjanesh/inertia-django-vite-react-minimal/">https://github.com/anjanesh/inertia-django-vite-react-minimal/</a></p>
]]></content:encoded></item><item><title><![CDATA[Port Forwarding from Linux on WSL to Windows]]></title><description><![CDATA[Here I have assumed that you know how to run a webserver in development mode on localhost like python manage.py runserver which runs on port 8000 by default or npm run dev which runs on port 3000 or 5173 etc or php -S localhost on port 80. I also ass...]]></description><link>https://anjanesh.dev/port-forwarding-from-linux-on-wsl-to-windows</link><guid isPermaLink="true">https://anjanesh.dev/port-forwarding-from-linux-on-wsl-to-windows</guid><category><![CDATA[port-forwarding]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[Linux]]></category><category><![CDATA[WSL]]></category><category><![CDATA[wsl2]]></category><category><![CDATA[terminal]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Fri, 31 Jan 2025 08:48:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/R54V69BN0MI/upload/7485f74d75e390f8534224748fbf28e2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here I have assumed that you know how to run a webserver in development mode on localhost like <code>python manage.py runserver</code> which runs on port 8000 by default or <code>npm run dev</code> which runs on port 3000 or 5173 etc or <code>php -S localhost</code> on port 80. I also assume you know how to run these on a different port like a custom, non-default port number.</p>
<p>Let's say you have on your <a target="_blank" href="https://learn.microsoft.com/en-us/windows/wsl/install">WSL</a> (which is Ubuntu by default, so I am assuming Ubuntu 24.04 here) running django as <code>python</code> <a target="_blank" href="http://manage.py"><code>manage.py</code></a> <code>runserver</code> on the default port of 8000.</p>
<p>Check for the IP Address on your WSL instance using <code>ifconfig</code>. Note down the IP address.</p>
<pre><code class="lang-javascript">$ ifconfig
<span class="hljs-attr">eth0</span>: flags=<span class="hljs-number">4163</span>&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="hljs-number">1500</span>
        inet <span class="hljs-number">112.12</span><span class="hljs-number">.14</span><span class="hljs-number">.190</span>  netmask <span class="hljs-number">255.255</span><span class="hljs-number">.240</span><span class="hljs-number">.0</span>  broadcast <span class="hljs-number">112.12</span><span class="hljs-number">.15</span><span class="hljs-number">.255</span>
        inet6 xxx::<span class="hljs-number">215</span>:xxx:xxx:<span class="hljs-number">6423</span>  prefixlen <span class="hljs-number">64</span>  scopeid <span class="hljs-number">0x20</span>&lt;link&gt;
        ether <span class="hljs-number">00</span>:xx:xx:xx:xx:<span class="hljs-number">23</span>  txqueuelen <span class="hljs-number">1000</span>  (Ethernet)
        RX packets <span class="hljs-number">3120</span>  bytes <span class="hljs-number">384154</span> (<span class="hljs-number">384.1</span> KB)
        RX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span>  overruns <span class="hljs-number">0</span>  frame <span class="hljs-number">0</span>
        TX packets <span class="hljs-number">1708</span>  bytes <span class="hljs-number">155068</span> (<span class="hljs-number">155.0</span> KB)
        TX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span> overruns <span class="hljs-number">0</span>  carrier <span class="hljs-number">0</span>  collisions <span class="hljs-number">0</span>

<span class="hljs-attr">lo</span>: flags=<span class="hljs-number">73</span>&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="hljs-number">65536</span>
        inet <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>  netmask <span class="hljs-number">255.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span>
        <span class="hljs-attr">inet6</span> ::<span class="hljs-number">1</span>  prefixlen <span class="hljs-number">128</span>  scopeid <span class="hljs-number">0x10</span>&lt;host&gt;
        loop  txqueuelen <span class="hljs-number">1000</span>  (Local Loopback)
        RX packets <span class="hljs-number">20</span>  bytes <span class="hljs-number">2102</span> (<span class="hljs-number">2.1</span> KB)
        RX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span>  overruns <span class="hljs-number">0</span>  frame <span class="hljs-number">0</span>
        TX packets <span class="hljs-number">20</span>  bytes <span class="hljs-number">2102</span> (<span class="hljs-number">2.1</span> KB)
        TX errors <span class="hljs-number">0</span>  dropped <span class="hljs-number">0</span> overruns <span class="hljs-number">0</span>  carrier <span class="hljs-number">0</span>  collisions <span class="hljs-number">0</span>
</code></pre>
<p>The IP address on this Ubuntu instance at WSL is <strong>112.12.14.190</strong>.</p>
<p>Now open PowerShell in administrator mode on your host (Windows 11 laptop) and enter</p>
<pre><code class="lang-javascript">netsh interface portproxy add v4tov4 listenport=<span class="hljs-number">8005</span> listenaddress=<span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span> connectport=<span class="hljs-number">8000</span> connectaddress=<span class="hljs-number">112.12</span><span class="hljs-number">.14</span><span class="hljs-number">.190</span>
</code></pre>
<p>This would listen to your WSL’s port 8000 and forward it to your Windows 11 at port 8005.</p>
<p>Now if you open the browser and enter http://localhost:8005 in the Address bar then you should be able to see the Django powered website running on your WSL in your Windows’ browser.</p>
<p>Now, if you want to stop listening to port 8005 or / and want to use port 8005 for something else, then you can de-register listening to port 8005 by :</p>
<pre><code class="lang-javascript">netsh interface portproxy <span class="hljs-keyword">delete</span> v4tov4 listenport=<span class="hljs-number">8005</span> listenaddress=<span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span>
</code></pre>
<p>Though I have mentioned WSL as the the OS that is the ‘guest’ on Windows, it can be applied to other platforms like <a target="_blank" href="https://www.vmware.com/">VMWare</a> or <a target="_blank" href="https://www.virtualbox.org/">VirtualBox</a> but most of these tools have a GUI for port-forwarding. Hence I’ve specifically mentioned WSL which is a 100% terminal based on Windows 10/11.</p>
]]></content:encoded></item><item><title><![CDATA[environment package in python]]></title><description><![CDATA[I think there are way too many environment handling packages out there (at PyPi) to get confused.

One is environ which was last released in 2007 - https://pypi.org/project/environ/ - which is Python 2 based. We don't want this anymore.

What we norm...]]></description><link>https://anjanesh.dev/environment-package-in-python</link><guid isPermaLink="true">https://anjanesh.dev/environment-package-in-python</guid><category><![CDATA[Environment variables]]></category><category><![CDATA[Environment]]></category><category><![CDATA[Python]]></category><category><![CDATA[Django]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Thu, 02 Jan 2025 04:07:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ieic5Tq8YMk/upload/fd3ad036c8d6a7adba6163f069549734.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I think there are way too many environment handling packages out there (at PyPi) to get confused.</p>
<ol>
<li><p>One is <code>environ</code> which was last released in 2007 - <a target="_blank" href="https://pypi.org/project/environ/">https://pypi.org/project/environ/</a> - which is Python 2 based. We don't want this anymore.</p>
</li>
<li><p>What we normally use in a pure python script is <code>python-environ</code> - <a target="_blank" href="https://pypi.org/project/python-environ/">https://pypi.org/project/python-environ/</a> - last released in 2020 - but the page is all about the next environ pypi package I've mentioned about - <code>django-environ</code></p>
</li>
<li><p>Then we have <code>django-environ</code> which is what is the latest - <a target="_blank" href="https://pypi.org/project/django-environ/">https://pypi.org/project/django-environ/</a> - last released in 2023 - this works perfectly on non-Django environments as well. Since its just a wrapper and also referred to by <code>python-environ</code> package page, we are better off using this package only.</p>
</li>
</ol>
<pre><code class="lang-powershell">python <span class="hljs-literal">-m</span> pip install environ
Collecting environ
  Downloading environ<span class="hljs-literal">-1</span>.<span class="hljs-number">0</span>.tar.gz (<span class="hljs-number">2.6</span> kB)
  Preparing metadata (setup.py) ... done
Installing collected packages: environ
  DEPRECATION: environ is being installed <span class="hljs-keyword">using</span> the legacy <span class="hljs-string">'setup.py install'</span> method, because it does not have a <span class="hljs-string">'pyproject.toml'</span> and the <span class="hljs-string">'wheel'</span> package is not installed. pip 23.1 will enforce this behaviour change. A possible replacement is to enable the <span class="hljs-string">'--use-pep517'</span> option. Discussion can be found at https://github.com/pypa/pip/issues/8559
  Running setup.py install <span class="hljs-keyword">for</span> environ ... done
Successfully installed environ<span class="hljs-literal">-1</span>.<span class="hljs-number">0</span>

(env) <span class="hljs-built_in">PS</span> D:\&gt; python .\test.py
Traceback (most recent call last):
  File <span class="hljs-string">"D:\test.py"</span>, line <span class="hljs-number">5</span>, <span class="hljs-keyword">in</span> &lt;module&gt;
    import environ
  File <span class="hljs-string">"D:\env\lib\site-packages\environ.py"</span>, line <span class="hljs-number">114</span>
    raise ValueError, <span class="hljs-string">"No frame marked with %s."</span> % fname
                    ^
SyntaxError: invalid syntax
(env) <span class="hljs-built_in">PS</span> D:\&gt; python <span class="hljs-literal">-m</span> pip uninstall environ
Found existing installation: environ <span class="hljs-number">1.0</span>
Uninstalling environ<span class="hljs-literal">-1</span>.<span class="hljs-number">0</span>:
  Would remove:
    d:\env\lib\site<span class="hljs-literal">-packages</span>\environ<span class="hljs-literal">-1</span>.<span class="hljs-number">0</span><span class="hljs-literal">-py3</span>.<span class="hljs-number">10</span>.egg<span class="hljs-literal">-info</span>
    d:\env\lib\site<span class="hljs-literal">-packages</span>\environ.py
Proceed (Y/n)? Y
  Successfully uninstalled environ<span class="hljs-literal">-1</span>.<span class="hljs-number">0</span>
(env) <span class="hljs-built_in">PS</span> D:\&gt; python .\test.py
Traceback (most recent call last):
  File <span class="hljs-string">"D:\test.py"</span>, line <span class="hljs-number">5</span>, <span class="hljs-keyword">in</span> &lt;module&gt;
    import environ
ModuleNotFoundError: No module named <span class="hljs-string">'environ'</span>
(env) <span class="hljs-built_in">PS</span> D:\&gt; python <span class="hljs-literal">-m</span> pip install python<span class="hljs-literal">-environ</span>
Collecting python<span class="hljs-literal">-environ</span>
  <span class="hljs-keyword">Using</span> cached python_environ-0.4.54-py2.py3-none-any.whl
Installing collected packages: python<span class="hljs-literal">-environ</span>
Successfully installed python<span class="hljs-literal">-environ</span><span class="hljs-literal">-0</span>.<span class="hljs-number">4.54</span>

(env) <span class="hljs-built_in">PS</span> D:\&gt; python <span class="hljs-literal">-m</span> pip uninstall python<span class="hljs-literal">-environ</span>
Found existing installation: python<span class="hljs-literal">-environ</span> <span class="hljs-number">0.4</span>.<span class="hljs-number">54</span>
Uninstalling python<span class="hljs-literal">-environ</span><span class="hljs-literal">-0</span>.<span class="hljs-number">4.54</span>:
  Would remove:
    d:\env\lib\site<span class="hljs-literal">-packages</span>\environ\*
    d:\env\lib\site<span class="hljs-literal">-packages</span>\python_environ<span class="hljs-literal">-0</span>.<span class="hljs-number">4.54</span>.dist<span class="hljs-literal">-info</span>\*
Proceed (Y/n)? Y
  Successfully uninstalled python<span class="hljs-literal">-environ</span><span class="hljs-literal">-0</span>.<span class="hljs-number">4.54</span>
(env) <span class="hljs-built_in">PS</span> D:\&gt; python <span class="hljs-literal">-m</span> pip install django<span class="hljs-literal">-environ</span>
Collecting django<span class="hljs-literal">-environ</span>
  <span class="hljs-keyword">Using</span> cached django_environ-0.11.2-py2.py3-none-any.whl (19 kB)
Installing collected packages: django<span class="hljs-literal">-environ</span>
Successfully installed django<span class="hljs-literal">-environ</span><span class="hljs-literal">-0</span>.<span class="hljs-number">11.2</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[apache2 and httpd being run on macOS simultaneously]]></title><description><![CDATA[This is a simple post mainly for my notes incase I need to recapitulate what should be done to restore running of my virtually hosted websites on my laptop.
Ever since I installed httpd on my macOS 15.1 using homebrew and configured my virtual hosts ...]]></description><link>https://anjanesh.dev/apache2-and-httpd-being-run-on-macos-simultaneously</link><guid isPermaLink="true">https://anjanesh.dev/apache2-and-httpd-being-run-on-macos-simultaneously</guid><category><![CDATA[apache2 ]]></category><category><![CDATA[httpd]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Homebrew]]></category><category><![CDATA[services]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Sun, 17 Nov 2024 12:51:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731847876758/a9bff1c1-b052-4e1d-a2fc-e22c364e6f7f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a simple post mainly for my <em>notes</em> incase I need to recapitulate what should be done to restore running of my virtually hosted websites on my laptop.</p>
<p>Ever since I installed <code>httpd</code> on my macOS 15.1 using <strong>homebrew</strong> and configured my virtual hosts on it and upon macOS updates which resulted in a restart of the laptop, my virtual hosts websites stopped working.</p>
<p>After many updates I found that there’s an <strong>apache2</strong> service also running in the background and upon going to local.myWebsite.com it was trying to detect the virtual host from Apache’s config and not the <code>httpd</code>’s one.</p>
<p>So I had to stop Apache2 and <code>httpd</code> and start <code>httpd</code>. Only then my virtual hosts were back running properly.</p>
<pre><code class="lang-bash">sudo apachectl stop
brew services stop httpd
brew services start httpd
</code></pre>
]]></content:encoded></item><item><title><![CDATA[DNS Records Query API Service from Google]]></title><description><![CDATA[Did you know Google has an API service to fetch DNS records ?
For example, if you want to get the MX records of techcrunch.com, you can get it from here : https://dns.google/query?name=techcrunch.com&rr_type=MX&ecs= where the API endpoint is at https...]]></description><link>https://anjanesh.dev/dns-records-query-api-service-from-google</link><guid isPermaLink="true">https://anjanesh.dev/dns-records-query-api-service-from-google</guid><category><![CDATA[dns]]></category><category><![CDATA[Google]]></category><category><![CDATA[APIs]]></category><category><![CDATA[domain]]></category><category><![CDATA[MX Record]]></category><category><![CDATA[cname records]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Sat, 24 Aug 2024 12:41:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724503086192/285ee80d-32b4-4f05-87b2-d09aa43403e3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Did you know Google has an API service to fetch DNS records ?</p>
<p>For example, if you want to get the MX records of techcrunch.com, you can get it from here : <a target="_blank" href="https://dns.google/query?name=techcrunch.com&amp;rr_type=MX&amp;ecs=">https://dns.google/query?name=techcrunch.com&amp;rr_type=MX&amp;ecs=</a> where the API endpoint is at <a target="_blank" href="https://dns.google/resolve?name=techcrunch.com&amp;type=MX">https://dns.google/resolve?name=techcrunch.com&amp;type=MX</a> after which you can parse the data.</p>
<p>And the best part ? - its CORS enabled. Meaning you can fetch from client side JavaScript itself from any IP address.</p>
<p>Here's a demo to play with : <a target="_blank" href="https://anjanesh.s3.amazonaws.com/demo/google-dns.html">https://anjanesh.s3.amazonaws.com/demo/google-dns.html</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724502901905/47ce229a-504d-46bb-8db7-9700095c15c7.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Convert HTML to PDF]]></title><description><![CDATA[One very frequently wanted requirement was to convert HTML to PDF. And all this while I was using mPDF which so far does a very good job in PDF creation. Though this one is PHP based, there are some equivalents out there for Python like xhtml2pdf and...]]></description><link>https://anjanesh.dev/html-to-pdf</link><guid isPermaLink="true">https://anjanesh.dev/html-to-pdf</guid><category><![CDATA[HTML5]]></category><category><![CDATA[CSS3]]></category><category><![CDATA[pdf]]></category><category><![CDATA[chromium]]></category><category><![CDATA[headless]]></category><category><![CDATA[Python]]></category><category><![CDATA[Converter]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Tue, 20 Aug 2024 14:58:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724165691811/3bd99061-6084-41f9-98ce-413bf6637b49.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One very frequently wanted requirement was to convert HTML to PDF. And all this while I was using <a target="_blank" href="https://github.com/mpdf/mpdf">mPDF</a> which so far does a very good job in PDF creation. Though this one is <mark>PHP</mark> based, there are some equivalents out there for Python like xhtml2pdf and WeasyPrint, but the more assured ones are which are browser-based libraries like <a target="_blank" href="https://pypi.org/project/pyhtml2pdf/">pyhtml2pdf</a> which has a dependency of <mark>Chrome webdriver</mark>. Now if you're on a desktop version of an OS and have Chrome installed then you're good but if you have to install Chrome webdriver on a server like Ubuntu, it can be a real nightmare.</p>
<p>Also, if you want to use the latest and greatest features available in HTML5 and CSS3 we need to use Chrome (Chromium) because mPDF's creator himself has mentioned on newer CSS support will most likely not be implemented - quote from <a target="_blank" href="https://github.com/mpdf/mpdf?tab=readme-ov-file#about-css-support-and-development-state">GitHub link</a> :</p>
<p><em>mPDF as a whole is a quite dated software. Nowadays, better alternatives are available, albeit not written in PHP.</em></p>
<p><em>Use mPDF if you cannot use non-PHP approach to generate PDF files or if you want to leverage some of the benefits of mPDF over browser approach – color handling, pre-print, barcodes support, headers and footers, page numbering, TOCs, etc. But beware that a HTML/CSS template tailored for mPDF might be necessary.</em></p>
<p><em>If you are looking for state of the art CSS support, mirroring existing HTML pages to PDF, use headless Chrome.</em></p>
<p><em>mPDF will still be updated to enhance some internal capabilities and to support newer versions of PHP, but better and/or newer CSS support will most likely not be implemented.</em></p>
<p>I was okay with not having what was new 5 years ago like <code>flex</code> and <code>grid</code> but not today.</p>
<p>Until I stumbled upon <a target="_blank" href="http://browserless.io">browserless.io</a> - it's like searching for a invisible needle in a burning haystack if you're not using the keyword <mark>docker</mark> in the search box. browserless does all the dirty work of installing not just chromium headless, but firefox and webkit headless as well. With browserless, you can open any URL or read an HTML page from a file parse the contents programmatically like you would normally do. The open-source code for the implementation is at <a target="_blank" href="https://github.com/browserless/browserless">https://github.com/browserless/browserless</a> and can be installed on your server.</p>
<p>So far, to HTML to PDF conversion in browserless is only supported by the chromium driver and not firefox and webkit drivers.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> <span class="hljs-built_in">test</span>
mkdir html2pdf
<span class="hljs-built_in">cd</span> html2pdf 
python3 -m venv env
<span class="hljs-built_in">source</span> env/bin/activate
pip install playwright
playwright install
</code></pre>
<p>The final <code>playwright install</code> should show something like this :</p>
<pre><code class="lang-bash">Downloading Chromium 128.0.6613.18 (playwright build v1129) from https://playwright.azureedge.net/builds/chromium/1129/chromium-mac-arm64.zip
138.1 MiB [====================] 100% 0.0s
Chromium 128.0.6613.18 (playwright build v1129) downloaded to /Users/anjanesh-m2/Library/Caches/ms-playwright/chromium-1129
Downloading FFMPEG playwright build v1009 from https://playwright.azureedge.net/builds/ffmpeg/1009/ffmpeg-mac-arm64.zip
1 MiB [====================] 100% 0.0s
FFMPEG playwright build v1009 downloaded to /Users/anjanesh-m2/Library/Caches/ms-playwright/ffmpeg-1009
Downloading Firefox 128.0 (playwright build v1458) from https://playwright.azureedge.net/builds/firefox/1458/firefox-mac-arm64.zip
79.6 MiB [====================] 100% 0.0s
Firefox 128.0 (playwright build v1458) downloaded to /Users/anjanesh-m2/Library/Caches/ms-playwright/firefox-1458
Downloading Webkit 18.0 (playwright build v2051) from https://playwright.azureedge.net/builds/webkit/2051/webkit-mac-14-arm64.zip
68.2 MiB [====================] 100% 0.0s
Webkit 18.0 (playwright build v2051) downloaded to /Users/anjanesh-m2/Library/Caches/ms-playwright/webkit-2051
</code></pre>
<p>If you're on Ubuntu it may ask you to install some dependencies which you can do so using <code>sudo apt-get install</code> - the list of dependencies will be mentioned right after when you execute <code>playwright install</code></p>
<p>Some of the dependencies for Ubuntu :</p>
<pre><code class="lang-bash">sudo apt-get install libwoff1 libopus0 libwebpdemux2 libharfbuzz-icu0 libenchant-2-2 libsecret-1-0 libhyphen0 libflite1 libegl1 libevdev2 libgles2 gstreamer1.0-libav
</code></pre>
<p>If you're experiencing difficulty on <mark>Windows</mark> try this :</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">cd</span> test
mkdir html2pdf
<span class="hljs-built_in">cd</span> html2pdf 
python <span class="hljs-literal">-m</span> venv env
.\env\Scripts\Activate.ps1
python <span class="hljs-literal">-m</span> pip install playwright
python <span class="hljs-literal">-m</span> pip show playwright
Name: playwright
Version: <span class="hljs-number">1.46</span>.<span class="hljs-number">0</span>
Summary: A high<span class="hljs-literal">-level</span> API to automate web browsers
Home<span class="hljs-literal">-page</span>: https://github.com/Microsoft/playwright<span class="hljs-literal">-python</span>
Author: Microsoft Corporation
Author<span class="hljs-literal">-email</span>:
License: Apache<span class="hljs-literal">-2</span>.<span class="hljs-number">0</span>
Location: C:\test\htm2pdf\env\Lib\site<span class="hljs-literal">-packages</span>
Requires: greenlet, pyee
Required<span class="hljs-literal">-by</span>:
<span class="hljs-built_in">cd</span> C:\test\htm2pdf\env\Scripts
playwright install
<span class="hljs-built_in">cd</span> C:\test\htm2pdf
</code></pre>
<p>Now create a file called <a target="_blank" href="http://convert.py">convert.py</a> with the following python code :</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> asyncio
<span class="hljs-keyword">from</span> playwright.async_api <span class="hljs-keyword">import</span> async_playwright

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">html_to_pdf</span>(<span class="hljs-params">html_file_path, pdf_file_path</span>):</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> async_playwright() <span class="hljs-keyword">as</span> p:
        browser = <span class="hljs-keyword">await</span> p.chromium.launch()
        page = <span class="hljs-keyword">await</span> browser.new_page()

        <span class="hljs-comment"># HTML file</span>
        <span class="hljs-keyword">with</span> open(html_file_path, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
            html_content = file.read()
        <span class="hljs-keyword">await</span> page.set_content(html_content)

        <span class="hljs-comment"># Save as PDF</span>
        <span class="hljs-keyword">await</span> page.pdf(path=pdf_file_path, print_background=<span class="hljs-literal">True</span>)

        <span class="hljs-keyword">await</span> browser.close()

html_file = <span class="hljs-string">'example.html'</span>
pdf_file = <span class="hljs-string">'output.pdf'</span>
asyncio.run(html_to_pdf(html_file, pdf_file))
</code></pre>
<p>Now download this example.html file either from <a target="_blank" href="https://anjanesh.s3.amazonaws.com/demo/example.html">my AWS S3 link</a> or my <a target="_blank" href="https://anjanesh.b-cdn.net/example.html">Bunny link</a>.</p>
<p>If you run <code>python</code> <a target="_blank" href="http://convert.py"><code>convert.py</code></a> you should see a PDF generated <a target="_blank" href="https://anjanesh.s3.amazonaws.com/demo/output.pdf">like this</a> in the same current folder.</p>
<p>If you're interested in running browserless as a standalone service in a separate app, and can spare atleast $5 a month, try using DigitalOcean App Platform where you can provide a <mark>GitHub Container Registry</mark> URL which is docker pull <a target="_blank" href="http://ghcr.io/browserless/base:latest">ghcr.io/browserless/base:latest</a> as mentioned here : <a target="_blank" href="https://github.com/browserless/browserless/pkgs/container/base">https://github.com/browserless/browserless/pkgs/container/base</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724159199351/9dc5f44f-4d27-4bc9-8b32-4e053402fe80.png" alt class="image--center mx-auto" /></p>
<p>Enter the following details (in red) :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724161971557/ec66d991-3ce5-4ddf-88a3-e8549ab8581c.png" alt class="image--center mx-auto" /></p>
<p>Use port 3000</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724159514612/0bd210ce-6a7b-426e-a393-a513d3da47f6.png" alt class="image--center mx-auto" /></p>
<p>This is the general idea - once docker is setup on a DigitalOcean App you can call the App URL as a service to send in the HTML content and generate the PDF as output.</p>
<p>Replace the URL (production-sfo.browserless.io) in the code mentioned here (<a target="_blank" href="https://docs.browserless.io/Libraries/playwright#python-playwright">https://docs.browserless.io/Libraries/playwright#python-playwright</a>) with the DigitalOcean App Platform URL. It should work. And instead of <code>p.firefox.connect</code> it should be <code>p.chromium.connect</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> playwright.sync_api <span class="hljs-keyword">import</span> sync_playwright

<span class="hljs-keyword">with</span> sync_playwright() <span class="hljs-keyword">as</span> p:
  browser = p.firefox.connect(<span class="hljs-string">'wss://production-sfo.browserless.io/firefox/playwright?token=GOES-HERE'</span>)
  context = browser.new_context()
  page = context.new_page()
  page.goto(<span class="hljs-string">'http://www.example.com'</span>, wait_until=<span class="hljs-string">'domcontentloaded'</span>)
  print(page.content())
  context.close()
</code></pre>
<p>When I was in college in Coimbatore way back in 2001 I went to the main street in RS Puram just walking past all the shops on one side of the road. I finally hit a sign that read <mark>Your Search Ends here</mark> and saw there were no more shops as it was more-or-less like a dead end. Hopefully your search for a PDF generator too ends here !</p>
]]></content:encoded></item><item><title><![CDATA[Using clean() in your Django models post migration from a legacy stack]]></title><description><![CDATA[If you're migrating an application from another stack where foreign keys were not enforced in the database and the some of the rows' foreign key fields have values as 0, then these would cause issues in the new application where the framework has str...]]></description><link>https://anjanesh.dev/using-clean-in-your-django-models-post-migration-from-a-legacy-stack</link><guid isPermaLink="true">https://anjanesh.dev/using-clean-in-your-django-models-post-migration-from-a-legacy-stack</guid><category><![CDATA[Django]]></category><category><![CDATA[models]]></category><category><![CDATA[orm]]></category><category><![CDATA[database]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Thu, 13 Jun 2024 00:48:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718239650471/238e4980-9740-4ec1-9775-be0eea8c1625.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're migrating an application from another stack where foreign keys were not enforced in the database and the some of the rows' foreign key fields have values as 0, then these would cause issues in the new application where the framework has strict checks and would throw an exception for 0 being saved as a foreign key value.</p>
<p>Let's take an example model from Django docs. <a target="_blank" href="https://docs.djangoproject.com/en/5.0/topics/db/examples/many_to_one/">https://docs.djangoproject.com/en/5.0/topics/db/examples/many_to_one/</a></p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span>(<span class="hljs-params">models.Model</span>):</span>
    headline = models.CharField(max_length=<span class="hljs-number">100</span>)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)

    media_house = models.ForeignKey(MediaHouse, on_delete=models.CASCADE, null=<span class="hljs-literal">True</span>, blank=<span class="hljs-literal">True</span>) <span class="hljs-comment"># Added, NEW</span>
    some_other_model = models.ForeignKey(SomeOtherModel, on_delete=models.CASCADE, null=<span class="hljs-literal">True</span>, blank=<span class="hljs-literal">True</span>) <span class="hljs-comment"># Added, NEW</span>
</code></pre>
<p>Let's say you have a row with primary key 915 and it contains <code>media_house_id</code> as 0 and <code>some_other_model_id</code> also as 0. Now let's just try to save this just after retrieving the row.</p>
<pre><code class="lang-python"><span class="hljs-keyword">try</span>:
    article = Article.objects.get(pk=<span class="hljs-number">915</span>)
    article.save()
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    msg = str(e)
</code></pre>
<p>You would get an exception.</p>
<blockquote>
<p>The database backend does not accept 0 as a value for AutoField.</p>
</blockquote>
<p>This is because the row with primary key ID 915 has an entry of <code>media_house_id</code> equal to 0 and/or <code>some_other_model_id</code> equal to 0 for which Foreign Key constraint fails as there isn't an entry (row) in the MediaHouse and SomeOtherModel tables where the primary key for those tables are 0.</p>
<p>So instead, we call the <code>clean</code> method (for which we write a <code>clean</code> function in the model) to set the field to None if it's 0.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span>(<span class="hljs-params">models.Model</span>):</span>
    headline = models.CharField(max_length=<span class="hljs-number">100</span>)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)

    media_house = models.ForeignKey(MediaHouse, on_delete=models.CASCADE, null=<span class="hljs-literal">True</span>, blank=<span class="hljs-literal">True</span>) <span class="hljs-comment"># Added, NEW</span>
    some_other_model = models.ForeignKey(SomeOtherModel, on_delete=models.CASCADE, null=<span class="hljs-literal">True</span>, blank=<span class="hljs-literal">True</span>) <span class="hljs-comment"># Added, NEW</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clean</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.media_house_id == <span class="hljs-number">0</span>:
            self.media_house_id = <span class="hljs-literal">None</span>
        <span class="hljs-keyword">if</span> self.some_other_model_id == <span class="hljs-number">0</span>:
            self.some_other_model_id = <span class="hljs-literal">None</span>
</code></pre>
<p>And before calling the <code>save()</code> method, we just call <code>clean()</code> as well.</p>
<pre><code class="lang-python"><span class="hljs-keyword">try</span>:
    article = Article.objects.get(pk=<span class="hljs-number">915</span>)
    article.clean() <span class="hljs-comment"># NEW : Call clean() to convert fields' invalid values to valid values or None (Null in MySQL)</span>
    article.save()    
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    msg = str(e)
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Targeting a button's form]]></title><description><![CDATA[I didn't know about this until yesterday but apparently you can target a form's button to another form.
So, there are 2 forms with IDs form-1 and form-2 : form-1's action is post-1.php and form-2's action is post-2.php
So when the Submit 2 button (wh...]]></description><link>https://anjanesh.dev/targeting-a-buttons-form</link><guid isPermaLink="true">https://anjanesh.dev/targeting-a-buttons-form</guid><category><![CDATA[HTML5]]></category><category><![CDATA[forms]]></category><category><![CDATA[button]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Mon, 27 May 2024 00:09:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716768508088/8c72adc8-fb6d-4618-866d-5db48959e398.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I didn't know about this until yesterday but apparently you can target a form's button to another form.</p>
<p>So, there are 2 forms with IDs <code>form-1</code> and <code>form-2</code> : <code>form-1</code>'s action is post-1.php and <code>form-2</code>'s action is post-2.php</p>
<p>So when the Submit 2 button (which is inside <code>form-1</code>) is clicked, it hits <code>form-2</code> which is post-2.php and not post-1.php</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"ie=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Button's Form Target<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">fieldset</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">legend</span>&gt;</span>Form 1<span class="hljs-tag">&lt;/<span class="hljs-name">legend</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form-1"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"post-1.php"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
            First Name : <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"first_name"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"first_name"</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"margin-top:10px; display: flex; column-gap: 5px;"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"form-1"</span>&gt;</span>Submit 1<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"form-2"</span>&gt;</span>Submit 2<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">fieldset</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">fieldset</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">legend</span>&gt;</span>Form 2<span class="hljs-tag">&lt;/<span class="hljs-name">legend</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form-2"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"post-2.php"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
            Last Name : <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"last_name"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"last_name"</span>/&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">fieldset</span>&gt;</span>    
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Using token in vanilla PHP for CSRF]]></title><description><![CDATA[Frameworks like Django and Laravel have CSRF Protection by default. Most of use it by their default behaviour in our jinja2 / blade templates and it just works. Super easy to make use of it and we need to write 0 code to implement the security bit on...]]></description><link>https://anjanesh.dev/using-token-in-vanilla-php-for-csrf</link><guid isPermaLink="true">https://anjanesh.dev/using-token-in-vanilla-php-for-csrf</guid><category><![CDATA[csrf]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Security]]></category><category><![CDATA[attacks]]></category><category><![CDATA[forms]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Sat, 04 May 2024 12:12:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714824497493/1cb1e6ab-51d9-4903-a5e9-674e9586af3b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Frameworks like Django and Laravel have <a target="_blank" href="https://laravel.com/docs/11.x/csrf">CSRF Protection</a> by default. Most of use it by their default behaviour in our jinja2 / blade templates and it just works. Super easy to make use of it and we need to write 0 code to implement the security bit on the form submission. <a target="_blank" href="https://laracasts.com/series/30-days-to-learn-laravel-11/episodes/16">Latest Laravel 11 Video Explanation on CSRF</a>.</p>
<p>But let's say you don't want to use a full-blown framework for a dead-simple scenario where the entire PHP application is just one <em>form</em> of sorts ... and still want to make use to CSRF - then, you would need to write it from scratch and implement the security bit.</p>
<pre><code class="lang-bash">touch form.php
touch form-post.php
code .
</code></pre>
<p><strong>form.php</strong></p>
<pre><code class="lang-php-template"><span class="php"><span class="hljs-meta">&lt;?php</span>
session_start();
$_SESSION[<span class="hljs-string">'token'</span>] = md5(uniqid(mt_rand(), <span class="hljs-literal">true</span>));
<span class="hljs-meta">?&gt;</span></span><span class="xml">
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Form<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"form-post.php"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">required</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"token"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"</span></span></span><span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> $_SESSION[<span class="hljs-string">'token'</span>] ?? <span class="hljs-string">''</span> <span class="hljs-meta">?&gt;</span></span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>    
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
</code></pre>
<p><strong>form-post.php</strong></p>
<pre><code class="lang-php-template"><span class="php"><span class="hljs-meta">&lt;?php</span>
session_start();

$token = htmlspecialchars($_POST[<span class="hljs-string">'token'</span>]);

<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>($token) || $token !== $_SESSION[<span class="hljs-string">'token'</span>])
{
    <span class="hljs-keyword">die</span>(<span class="hljs-string">"Attack attempted"</span>);
}

<span class="hljs-comment"># Re-Generate token so that hitting the refresh button invalidates the form submission</span>
$_SESSION[<span class="hljs-string">'token'</span>] = md5(uniqid(mt_rand(), <span class="hljs-literal">true</span>));
<span class="hljs-meta">?&gt;</span></span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Form POSTed<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</span><span class="php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;pre&gt;POST : \n"</span>;
print_r($_POST); <span class="hljs-keyword">echo</span> <span class="hljs-string">"\n"</span>;
<span class="hljs-keyword">echo</span> <span class="hljs-string">"SESSION :\n"</span>;
print_r($_SESSION);
<span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;/pre&gt;"</span>
<span class="hljs-meta">?&gt;</span></span>
</code></pre>
<pre><code class="lang-bash">php -S localhost:8004
</code></pre>
<p>Now browse to <a target="_blank" href="http://localhost:8004/form.php">http://localhost:8004/form.php</a> and submit and your should see a Form POSTed and now if you hit the refresh button - you should see a "Attack attempted" message. This is because a new token got generated on form submit and it doesn't match with the one in the session's data.</p>
]]></content:encoded></item><item><title><![CDATA[Without using document.getElementById]]></title><description><![CDATA[A few weeks ago I was surprised at at the fact that something that seemed impossible was / is still possible.
I didn't realize that this was even possible in my 20+ years of web development. I even had a debate with my colleague about not using it th...]]></description><link>https://anjanesh.dev/without-using-documentgetelementbyid</link><guid isPermaLink="true">https://anjanesh.dev/without-using-documentgetelementbyid</guid><category><![CDATA[getElementById]]></category><category><![CDATA[HTML]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[window object]]></category><category><![CDATA[Browsers]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Mon, 04 Mar 2024 15:50:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709567366096/6e5dafe2-03e3-483c-a9b1-0f5dbb3e0146.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few weeks ago I was surprised at at the fact that something that seemed impossible was / is still possible.</p>
<p>I didn't realize that this was even possible in my 20+ years of web development. I even had a debate with my colleague about not using it that way but we weren't in sync.</p>
<p>Apparently - if you set in your HTML <code>&lt;input type="text" id="first_name" /&gt;</code> , you don't need to do <code>document.getElementById('first_name').value</code> - instead, you can directly call <code>first_name.value</code> !!!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/anjanesh/pen/GRLJZrj">https://codepen.io/anjanesh/pen/GRLJZrj</a></div>
<p> </p>
<p><a target="_blank" href="https://codepen.io/anjanesh/pen/GRLJZrj">https://codepen.io/anjanesh/pen/GRLJZrj</a></p>
<p>Apparently, this is still valid and is also in the <a target="_blank" href="https://html.spec.whatwg.org/multipage/nav-history-apis.html#named-access-on-the-window-object">HTML spec</a>.</p>
<blockquote>
<p>the value of the id content attribute for all HTML elements that have a non-empty id content attribute and are in a document tree with window's associated Document as their root.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Accessing an array list in a Django jinja2 template]]></title><description><![CDATA[This may seem miniscule, but many newbies tend to forget the fact that template coding in HTML is not necessarily equivalent to language code.
In almost all programming languages (or probably all ?), we access an index of an array / list using the sq...]]></description><link>https://anjanesh.dev/accessing-an-array-list-in-a-django-jinja2-template</link><guid isPermaLink="true">https://anjanesh.dev/accessing-an-array-list-in-a-django-jinja2-template</guid><category><![CDATA[Django]]></category><category><![CDATA[Python]]></category><category><![CDATA[Jinja2]]></category><category><![CDATA[fruits]]></category><category><![CDATA[template]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Fri, 16 Feb 2024 15:28:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708097166662/1be6fcae-e231-48de-8465-388b017e12a4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This may seem miniscule, but many newbies tend to forget the fact that template coding in HTML is not necessarily equivalent to language code.</p>
<p>In almost all programming languages (or probably all ?), we access an index of an array / list using the square brackets notation with the index number in between.</p>
<p>For example, in python code we would do :</p>
<pre><code class="lang-python">fruits = [<span class="hljs-string">'🍎'</span>, <span class="hljs-string">'🍐'</span>, <span class="hljs-string">'🍌'</span>]
print(fruits[<span class="hljs-number">1</span>]) <span class="hljs-comment"># will print 🍐</span>
</code></pre>
<p>In a Django, which uses Jinja2 templating by default, when we set the values in context and send it across to the template, we use dot followed by the index to access the array index like this :</p>
<pre><code class="lang-xml">{{ fruits.1 }}
</code></pre>
<p>So, if we have something like this which has the fruit and the price of it, in the context,</p>
<pre><code class="lang-python">fruits = [[<span class="hljs-string">'🍎'</span>, <span class="hljs-number">50</span>], [<span class="hljs-string">'🍐'</span>, <span class="hljs-number">35</span>], [<span class="hljs-string">'🍌'</span>, <span class="hljs-number">22</span>]]
context = {<span class="hljs-string">'fruitsWithPrice'</span>: fruits}
</code></pre>
<p>then we would need to use <code>fruit.0</code> and <code>fruit.1</code> for each fruit.</p>
<pre><code class="lang-xml">{% for fruit in fruitsWithPrice %}
    {{ fruit.0 }} - ${{ fruit.1 }}<span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
{% endfor %}
</code></pre>
<p>This is something I myself didn't know for a while too.</p>
]]></content:encoded></item><item><title><![CDATA[Proficiency Tester]]></title><description><![CDATA[Today I got my EXE program, which I wrote in Visual Basic 6.0 for SITRA (South India Textile Research Association) Coimbatore, way back in 2002, running on my Windows 11 machine which I thought would never happen.
Download the relevant OCX files from...]]></description><link>https://anjanesh.dev/proficiency-tester</link><guid isPermaLink="true">https://anjanesh.dev/proficiency-tester</guid><category><![CDATA[SITRA]]></category><category><![CDATA[proficiency test]]></category><category><![CDATA[windows 2000]]></category><category><![CDATA[visual basic]]></category><category><![CDATA[statistics]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Fri, 26 Jan 2024 11:42:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706268911811/027dc9be-ec0e-4a57-a861-cfd4feebdd40.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today I got my EXE program, which I wrote in Visual Basic 6.0 for SITRA (South India Textile Research Association) Coimbatore, way back in 2002, running on my Windows 11 machine which I thought would never happen.</p>
<p>Download the relevant OCX files from :</p>
<ul>
<li><p><a target="_blank" href="https://www.ocxme.com/files/MSCOMCTL">https://www.ocxme.com/files/MSCOMCTL</a></p>
</li>
<li><p><a target="_blank" href="https://www.ocxme.com/files/comdlg32">https://www.ocxme.com/files/comdlg32</a></p>
</li>
<li><p><a target="_blank" href="https://www.ocxme.com/files/msflexgrid">https://www.ocxme.com/files/msflexgrid</a></p>
</li>
</ul>
<p>and move them to this folder : C:\Windows\SysWOW64\</p>
<p>Now run this in command prompt (cmd.exe) as an Administrator :</p>
<pre><code class="lang-plaintext">cd C:\Windows\System32
C:\Windows\System32&gt;regsvr32.exe C:\Windows\SysWOW64\MSCOMCTL.OCX
C:\Windows\System32&gt;regsvr32.exe C:\Windows\SysWOW64\comdlg32.ocx
C:\Windows\System32&gt;regsvr32.exe C:\Windows\SysWOW64\msflexgrid.ocx
</code></pre>
<p>Now run ProficiencyTest.exe - this uses text files as a database - this was at a time when I didn't know about SQL / RDMS (even though we had a subject on it !) and has no requirement to an internet connection - used books to learn - no StackOverflow, no YouTube, no ChatGPT and lastly, hardly any Google because Internet was not available (most of the time) until 2003 ! - I used Yahoo! in college but I don't recollect searching for programming solutions from the Internet - all I had was a VB6 book. And of course, my college's lab assistants helped me a lot in troubleshooting setup and configuration issues.</p>
<p>PS: Their site, <a target="_blank" href="https://www.sitraindia.org/">https://www.sitraindia.org/</a> which was active in 2002 is no longer valid - the current site is <a target="_blank" href="http://sitra.org.in">sitra.org.in</a></p>
<p>Most of the functions / menus don't work because of the UI being very outdated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706269234927/640e85ae-4ac8-4a46-bc7a-5d711e28a940.png" alt class="image--center mx-auto" /></p>
<p>Source : <a target="_blank" href="https://github.com/anjanesh/sitra">https://github.com/anjanesh/sitra</a></p>
]]></content:encoded></item><item><title><![CDATA[onhover Larger Image Popup / ToolTip]]></title><description><![CDATA[Something I've been searching for a long time was to have a larger image shown on mouse hover the thumbnail image using TailWindCSS. I could only find a solution using Flowbite's popover component which requires JavaScript. Hopefully I can update thi...]]></description><link>https://anjanesh.dev/onhover-larger-image-popup-tooltip</link><guid isPermaLink="true">https://anjanesh.dev/onhover-larger-image-popup-tooltip</guid><category><![CDATA[images]]></category><category><![CDATA[HTML]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Flowbite]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Tue, 23 Jan 2024 03:26:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705980282079/d7d57e34-81b8-426e-b3b3-81d662a6b15e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Something I've been searching for a long time was to have a larger image shown on mouse hover the thumbnail image using TailWindCSS. I could only find a solution using <a target="_blank" href="https://flowbite.com/docs/components/popover/">Flowbite's popover component</a> which requires JavaScript. Hopefully I can update this post in the future when this can be achieved using CSS/TailWindCSS alone.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>onhover Larger Image Popup / ToolTip<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tailwindcss.com"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>    
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-5 ml-5 ring-2 ring-offset-2 group block w-32 aspect-w-10 aspect-h-7 rounded-lg bg-gray-100 overflow-hidden"</span>
        <span class="hljs-attr">data-popover-target</span>=<span class="hljs-string">"popover-default-1"</span>
    &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"https://anjanesh.s3.amazonaws.com/island.webp"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"Island"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"object-cover pointer-events-none"</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">"150"</span>
        /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">data-popover</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"popover-default-1"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"tooltip"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute z-10 invisible inline-block w-64 text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-sm opacity-0 dark:text-gray-400 dark:border-gray-600 dark:bg-gray-800"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-3 py-2 bg-gray-100 border-b border-gray-200 rounded-t-lg dark:border-gray-600 dark:bg-gray-700"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-gray-900 dark:text-white"</span>&gt;</span>Island<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-3 py-2"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                <span class="hljs-attr">src</span>=<span class="hljs-string">"https://anjanesh.s3.amazonaws.com/island.webp"</span>
                <span class="hljs-attr">alt</span>=<span class="hljs-string">"Island"</span>
                <span class="hljs-attr">class</span>=<span class="hljs-string">"object-cover pointer-events-none"</span>
                <span class="hljs-attr">width</span>=<span class="hljs-string">"750"</span>
            /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>        
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<ol>
<li><p>Don't use https://cdn.tailwindcss.com in production.</p>
</li>
<li><p>Also, for the thumbnail image, use an appropriate thumbnail sized version of the image instead of shrinking it using the width or height attributes.</p>
</li>
</ol>
<p>Demo : <a target="_blank" href="https://anjanesh.s3.amazonaws.com/demo/onhover-Larger-Image-Popup-ToolTip.html">https://anjanesh.s3.amazonaws.com/demo/onhover-Larger-Image-Popup-ToolTip.html</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705980312622/3e64fd7d-8b95-43ed-8622-39ef86c426ed.jpeg" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Setting the user.request object to an Azure AD user]]></title><description><![CDATA[I am using Azure Active Directory for sign-in in my Django application. There is an entire tutorial on how to set this up at Microsoft's website - https://learn.microsoft.com/en-us/training/modules/msid-django-web-app-sign-in/
Now that we can sign-in...]]></description><link>https://anjanesh.dev/setting-the-userrequest-object-to-an-azure-ad-user</link><guid isPermaLink="true">https://anjanesh.dev/setting-the-userrequest-object-to-an-azure-ad-user</guid><category><![CDATA[Django]]></category><category><![CDATA[azure-active-directory]]></category><category><![CDATA[Middleware]]></category><category><![CDATA[users]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Fri, 25 Aug 2023 02:53:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692931880192/0b605cc5-e586-4701-95c7-b8975c923cd0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am using Azure Active Directory for sign-in in my Django application. There is an entire tutorial on how to set this up at Microsoft's website - <a target="_blank" href="https://learn.microsoft.com/en-us/training/modules/msid-django-web-app-sign-in/">https://learn.microsoft.com/en-us/training/modules/msid-django-web-app-sign-in/</a></p>
<p>Now that we can sign-in using Azure AD, the Django default sign-in is left as is, and not used. But there still is a <code>auth_user</code> table created during <code>makemigrations</code> and <code>migrate</code>. the <code>auth_user</code> table is the one that Django references for the default Djangp's built-in user login.</p>
<p>If you are syncing the users in Django's default <code>auth_user</code> table with the Azure AD usernames, then you can auto-signin to the Django user system post logging in via Azure AD.</p>
<p>This is achieved by integrating a custom middleware.</p>
<p>Assuming your app's name is <code>application</code>, create a file called custom_middleware.py in the <code>application</code> folder.</p>
<p>Let us create a function containing the following code :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getAzureADDetails</span>(<span class="hljs-params">request</span>):</span>
    claims = request.identity_context_data._id_token_claims
    exclude_claims = [<span class="hljs-string">'iat'</span>, <span class="hljs-string">'exp'</span>, <span class="hljs-string">'nbf'</span>, <span class="hljs-string">'uti'</span>, <span class="hljs-string">'aio'</span>, <span class="hljs-string">'rh'</span>]
    claims_to_display = {claim: value <span class="hljs-keyword">for</span> claim, value <span class="hljs-keyword">in</span> claims.items() <span class="hljs-keyword">if</span> claim <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> exclude_claims}
    <span class="hljs-keyword">return</span> dict(claims_to_display)
</code></pre>
<p>The above is Microsoft's standard code for extracting user login info from the Azure AD session.</p>
<p>Now let's create the middleware class. The middleware class requires <code>__call__</code> to be defined and a response to be returned.</p>
<p>Code before <code>response = self.get_response(request)</code> would be executed before the view is rendered, while code after <code>response = self.get_response(request)</code> would be executed after the view the rendered. We specifically need our logic to be inserted <strong><em>after</em></strong> <code>response = self.get_response(request)</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> ms_identity_web <span class="hljs-keyword">import</span> IdentityWebPython

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomMiddleware</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, get_response</span>):</span>
        self.get_response = get_response

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, request</span>):</span>
        print(<span class="hljs-string">"Middleware: Custom code is running!"</span>)

        <span class="hljs-comment"># Code to run before the view is called</span>
        response = self.get_response(request)
        <span class="hljs-comment"># Code to run after the view is called and before the response is returned</span>

        azureAD = getAzureADDetails(request)

        <span class="hljs-comment"># A user has logged in to the django application via the AD login</span>
        <span class="hljs-keyword">if</span> len(azureAD) &gt; <span class="hljs-number">0</span>:

            claims = request.identity_context_data._id_token_claims

            <span class="hljs-keyword">try</span>:
                user = User.objects.get(email=claims[<span class="hljs-string">'preferred_username'</span>])
                request.user = user
            <span class="hljs-keyword">except</span> User.DoesNotExist:
                print(<span class="hljs-string">"not able to find AD user from django's auth_users table"</span>)

        <span class="hljs-keyword">return</span> response
</code></pre>
<p>So now enter <code>'application.custom_middleware.CustomMiddleware',</code> in the MIDDLEWARE list in settings.py. For example, it should look similar to this sample snippet :</p>
<pre><code class="lang-python">MIDDLEWARE = [
    <span class="hljs-string">'debug_toolbar.middleware.DebugToolbarMiddleware'</span>,
    <span class="hljs-string">'django.middleware.security.SecurityMiddleware'</span>,
    <span class="hljs-string">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
    <span class="hljs-string">'django.middleware.common.CommonMiddleware'</span>,
    <span class="hljs-string">'django.middleware.csrf.CsrfViewMiddleware'</span>,
    <span class="hljs-string">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,
    <span class="hljs-string">'application.custom_middleware.CustomMiddleware'</span>, <span class="hljs-comment"># This was inserted</span>
    <span class="hljs-string">'django.contrib.messages.middleware.MessageMiddleware'</span>,
    <span class="hljs-string">'django.middleware.clickjacking.XFrameOptionsMiddleware'</span>,
]
</code></pre>
<p>Now, in views.py <code>request.user.is_authenticated</code> would be <code>True</code> when someone logs into your Django app via Microsoft's Azure Active Directory.</p>
<p>Note: here I have assumed that your <code>auth_users</code> table has the data filled in with your Azure Active Directory users. This can be done by an <strong>insert</strong> from another source when a new AD user logs in or is created.</p>
]]></content:encoded></item><item><title><![CDATA[Simplest Example of HTMX]]></title><description><![CDATA[HTMX is one of the less popular libraries/adapters out there - in fact there isn't yet a wikipedia page on HTMX. Just as Interia is to Laravel, HTMX is to Django - but this is a misconception. Intertia can be used in Django as well and HTMX can be us...]]></description><link>https://anjanesh.dev/htmx</link><guid isPermaLink="true">https://anjanesh.dev/htmx</guid><category><![CDATA[HTML]]></category><category><![CDATA[Ajax]]></category><category><![CDATA[PHP]]></category><category><![CDATA[htmx]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Anjanesh Lekshminarayanan]]></dc:creator><pubDate>Tue, 25 Jul 2023 02:02:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690249666695/cf4dcfcf-f984-4074-b9d4-76f8b517968a.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://htmx.org/">HTMX</a> is one of the less popular libraries/adapters out there - in fact there isn't yet a wikipedia page on HTMX. Just as <a target="_blank" href="https://inertiajs.com/">Interia</a> is to Laravel, HTMX is to Django - but this is a misconception. Intertia can be used in <a target="_blank" href="https://github.com/inertiajs/inertia-django">Django</a> as well and HTMX can be used in Laravel too.</p>
<p>HTMX is JavaScript-less AJAX for the end-user (developer). Under the hood, HTMX uses JavaScript by including its <a target="_blank" href="https://unpkg.com/htmx.org@1.9.3/dist/htmx.js">library</a>.</p>
<p>The simplest example of using HTMX is by having an HTML <code>&lt;form&gt;</code> in your web page and some inputs a standard submit button. The JavaScript way to handle form submissions is the use <code>e.preventdefault()</code> React, Angular, Vue or Svelte and have client-side validation and then submit the form via JSON via <code>axios</code> or the like and get back a JSON response to the browser which the client-side framework will parse and re-render the component to update the DOM. All this is without refreshing the page via JavaScript code.</p>
<p>But the same non-refreshing page effect can be achieved via HTMX, except that, instead of returning a JSON from the server API call, we render back an HTML snippet from the server that's sent back to the browser and the entire HTML snippet is inserted into the webpage without page re-loading. It's still AJAX under the hood, but instead of JSON, it's HTML and you can do server-side validation just as if like you're submitting a traditional HTML form.</p>
<p>Here's an example (using vanilla PHP) where there exists a timer on the page just to show that the page doesn't get refreshed on clicking the submit button in the form - a full page refresh would reset the timer back to 0.</p>
<p>form-page.php :</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>HTMX Example<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/htmx.org@1.9.3"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mt-5 border border-danger"</span>&gt;</span>

    Content in red DIV with live time does not reset.<span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">let</span> timer = <span class="hljs-number">0</span>;    
    <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span>
    {        
        timer += <span class="hljs-number">1</span>;
        <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'time'</span>).innerHTML = timer;
    }, <span class="hljs-number">1000</span>);
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2"</span>&gt;</span>Timer : <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"time"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">form</span>
        <span class="hljs-attr">hx-post</span>=<span class="hljs-string">"form-post.php"</span>
        <span class="hljs-attr">hx-target</span>=<span class="hljs-string">"this"</span>
        <span class="hljs-attr">hx-swap</span>=<span class="hljs-string">"innerHTML"</span>
    &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">?php</span> <span class="hljs-attr">require_once</span> "<span class="hljs-attr">form-post.php</span>" ?&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>


<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>The following php snippet inserted into the <strong>form-page</strong> above will be used for both GET and POST. Ideally, you would render different files in Laravel based on the REQUEST type.</p>
<p>form-post.php :</p>
<pre><code class="lang-php-template"><span class="php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>($_POST[<span class="hljs-string">'name'</span>]) &amp;&amp; <span class="hljs-keyword">isset</span>($_POST[<span class="hljs-string">'email'</span>]))
{
    <span class="hljs-meta">?&gt;</span></span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-2"</span>&gt;</span>        
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Data POSTed<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            Name : </span><span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> $_POST[<span class="hljs-string">'name'</span>] <span class="hljs-meta">?&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
            Email : </span><span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> $_POST[<span class="hljs-string">'email'</span>] <span class="hljs-meta">?&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
            <span class="hljs-attr">hx-get</span>=<span class="hljs-string">"form-post.php"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-3 btn btn-primary"</span>
        &gt;</span>
            Add Data again
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    </span><span class="php"><span class="hljs-meta">&lt;?php</span>
}
<span class="hljs-keyword">else</span>
{
    <span class="hljs-meta">?&gt;</span></span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-2"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group py-2"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"txt-name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"txt-Name"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your name"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group py-2"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"txt-Email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"txt-Email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your email address"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>        
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary mt-1"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    </span><span class="php"><span class="hljs-meta">&lt;?php</span>
}
<span class="hljs-meta">?&gt;</span></span>
</code></pre>
<p>Run this as <code>php -S localhost:8001</code> in the terminal powershell and goto <code>http://localhost:8001/form-page.php</code> and click the submit button to see it in action - the timer will continue as it is (not from 0) since it's not a full page refresh.</p>
<details><summary>hx-post="form-post.php"</summary><div data-type="detailsContent">hit the form-post.php page when the submit button is clicked</div></details><details><summary>hx-target="this"</summary><div data-type="detailsContent">target is the same HTML Element which is this current <code>&lt;form&gt;</code> - you can have the HTML snippet returned to be updated on another DOM element though and it doesn't necessarily have to be a <code>&lt;form&gt;</code>, it can be a <code>&lt;div&gt;</code> as well.</div></details><details><summary>hx-swap="innerHTML"</summary><div data-type="detailsContent">innerHTML specifies that only the <code>&lt;form&gt;</code>'s innerHTML to be replaced and not the entire <code>&lt;form hx-post="form-post.php" hx-target="this" hx-swap="innerHTML"&gt; ... &lt;/form&gt;</code>, hx-swap="outerHTML" will replace the <code>&lt;form hx-post="form-post.php" hx-target="this" hx-swap="innerHTML"&gt; ... &lt;/form&gt;</code> as well.</div></details>]]></content:encoded></item></channel></rss>