<?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[Clever Wizard | Mastering Solutions]]></title><description><![CDATA[A blog dedicated to simplifying complex business scenarios using Dynamics 365 CE, Power Platform, and Azure, empowering developers and organizations to implement innovative solutions with ease.]]></description><link>https://blog.cleverwizard.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1756909581049/65303308-e8e3-49bc-9d32-447e3359220a.png</url><title>Clever Wizard | Mastering Solutions</title><link>https://blog.cleverwizard.com</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 10 Sep 2026 13:39:32 GMT</lastBuildDate><atom:link href="https://blog.cleverwizard.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Retrieve High-Resolution Images in Dynamics 365 Plugins & Code Activities]]></title><description><![CDATA[When developing Plugins or Custom Workflow Activities in Dynamics 365 CE or PowerApps (Dataverse), a common roadblock is retrieving image fields. If you access an image attribute using standard Retrie]]></description><link>https://blog.cleverwizard.com/how-to-retrieve-high-resolution-images-in-dynamics-365-plugins-code-activities</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-retrieve-high-resolution-images-in-dynamics-365-plugins-code-activities</guid><category><![CDATA[ms crm]]></category><category><![CDATA[dynamics 365 crm]]></category><category><![CDATA[Dynamics 365]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[C#]]></category><category><![CDATA[plugins]]></category><category><![CDATA[code]]></category><category><![CDATA[Dynamics CE]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Wed, 27 May 2026 09:41:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/7a9658b8-2f36-4449-9cab-83862f9905b0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When developing <strong>Plugins</strong> or <strong>Custom Workflow Activities</strong> in Dynamics 365 CE or PowerApps (Dataverse), a common roadblock is retrieving image fields. If you access an image attribute using standard <code>Retrieve</code> or <code>RetrieveMultiple(FetchXML)</code> logic, Dataverse returns a low-resolution <strong>thumbnail</strong> by default.</p>
<p>To get the <strong>original, full-resolution image</strong>, you must use the file streaming message requests. This post provides a clean, reusable approach to extracting full image data as either a <code>Base64</code> string or raw <code>byte[]</code> data.</p>
<hr />
<h3>The Problem: The "Thumbnail Trap"</h3>
<p>In Dataverse, image fields are optimized for performance. When you call <code>entity.GetAttributeValue&lt;byte[]&gt;("your_field")</code>, the platform serves a compressed version to save bandwidth. For high-quality email signatures, document generation, or external integrations, this thumbnail is usually too blurry to use.</p>
<h3>The Solution: Multi-Block Downloading</h3>
<p>To bypass compression, you must treat the image as a file and download it in "blocks" (chunks). This ensures you pull the raw, uncompressed data directly from the storage layer.</p>
<hr />
<h2>The Implementation</h2>
<p>Copy the following methods into your helper class. This logic is sandbox-safe and designed for high performance within the 2-minute plugin execution limit.</p>
<p><em>(Note: Ensure you have</em> <code>Microsoft.Crm.Sdk.Messages</code> <em>included in your using statements).</em></p>
<h3>1. The Entry Function: <code>GetFullImageContent</code></h3>
<p>This is the method you will call from your main logic. It allows you to toggle the return format using a Boolean parameter.</p>
<pre><code class="language-csharp">/// &lt;summary&gt;
/// Retrieves full-resolution image data from a record.
/// &lt;/summary&gt;
/// &lt;param name="service"&gt;The IOrganizationService instance.&lt;/param&gt;
/// &lt;param name="entityName"&gt;Logical name of the entity (e.g., "account").&lt;/param&gt;
/// &lt;param name="recordId"&gt;The GUID of the specific record.&lt;/param&gt;
/// &lt;param name="imageFieldName"&gt;The logical name of the image attribute.&lt;/param&gt;
/// &lt;param name="asBase64"&gt;Set to true for Base64 string, false for byte array string.&lt;/param&gt;
/// &lt;returns&gt;A string containing the image data.&lt;/returns&gt;
public string GetFullImageContent(IOrganizationService service, string entityName, Guid recordId, string imageFieldName, bool asBase64)
{
    if (recordId == Guid.Empty || string.IsNullOrEmpty(entityName) || string.IsNullOrEmpty(imageFieldName))
        return string.Empty;

    // Create the reference to the target record
    EntityReference recordRef = new EntityReference(entityName, recordId);

    // Call the helper to stream the full bytes
    byte[] imageBytes = DownloadFullImage(service, recordRef, imageFieldName);

    if (imageBytes == null || imageBytes.Length == 0)
        return string.Empty;

    // Return the format requested by the caller
    return asBase64 ? Convert.ToBase64String(imageBytes) : BitConverter.ToString(imageBytes);
}
</code></pre>
<h3>2. The Internal Streaming Helper: <code>DownloadFullImage</code></h3>
<p>This method manages the initialization and the loop required to piece the image chunks back together.</p>
<pre><code class="language-csharp">private byte[] DownloadFullImage(IOrganizationService service, EntityReference recordRef, string attributeName)
{
    // 1. Initialize the download session
    var initializeRequest = new InitializeFileBlocksDownloadRequest()
    {
        Target = recordRef,
        FileAttributeName = attributeName
    };

    var initializeResponse = (InitializeFileBlocksDownloadResponse)service.Execute(initializeRequest);

    string fileToken = initializeResponse.FileContinuationToken;
    long fileSize = initializeResponse.FileSizeInBytes;
    List&lt;byte&gt; fullFile = new List&lt;byte&gt;((int)fileSize);

    long offset = 0;
    long blockSize = 4 * 1024 * 1024; // 4 MB Chunk Size

    // 2. Loop through and download blocks until complete
    while (offset &lt; fileSize)
    {
        var downloadBlockRequest = new DownloadBlockRequest()
        {
            BlockLength = blockSize,
            FileContinuationToken = fileToken,
            Offset = offset
        };

        var downloadBlockResponse = (DownloadBlockResponse)service.Execute(downloadBlockRequest);
        
        fullFile.AddRange(downloadBlockResponse.Data);
        
        // Move the offset forward by the actual amount of data received
        offset += downloadBlockResponse.Data.Length;
    }

    return fullFile.ToArray();
}
</code></pre>
<h3>Key Technical Considerations</h3>
<ul>
<li><p><strong>Enable "Store Full Image":</strong> This code will only return the high-resolution file if the "Store full image" option is enabled in the column's definition within the Power Apps Maker Portal.</p>
</li>
<li><p><strong>SDK Versions:</strong> Ensure your project is referencing <code>Microsoft.CrmSdk.CoreAssemblies</code> (v9.0.2.x or higher) to access the <code>InitializeFileBlocksDownloadRequest</code> and <code>DownloadBlockRequest</code> classes.</p>
</li>
<li><p><strong>Performance:</strong> While 4MB is the standard block size, the code will automatically handle smaller files in a single pass.</p>
</li>
<li><p><strong>Base64 Use Case:</strong> Use <code>asBase64 = true</code> if you are injecting the image into an HTML template or returning it via a Web API.</p>
</li>
<li><p><strong>Error Handling:</strong> In a production environment, always wrap these calls in a <code>try-catch</code> block and use the <code>ITracingService</code> to monitor the <code>fileSize</code> and <code>offset</code> during execution.</p>
</li>
</ul>
<hr />
<h3>Summary</h3>
<p>By ensuring your Dataverse columns are set to <strong>Store full image</strong> and using the <strong>Initialize/Download pattern</strong> instead of direct attribute access, you can ensure your Dynamics 365 solutions handle images with the highest possible fidelity.</p>
<p>Happy coding, Wizards!</p>
]]></content:encoded></item><item><title><![CDATA[How to Find Who Modified (and Created) a Plugin in Dynamics 365 CE / Power Platform (Dataverse)]]></title><description><![CDATA[In this post, we’ll look at how to query the Plugin Assembly entity directly to retrieve the creator and modifier details within Dynamics 365 CE / Power Platform using a simple GET request.

The Chall]]></description><link>https://blog.cleverwizard.com/how-to-find-who-modified-and-created-a-plugin-in-dynamics-365-ce-power-platform-dataverse</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-find-who-modified-and-created-a-plugin-in-dynamics-365-ce-power-platform-dataverse</guid><category><![CDATA[Dynamics 365 CE]]></category><category><![CDATA[Microsoft Dynamics 365 Certification]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[Power Platform]]></category><category><![CDATA[Power Platform Solutions]]></category><category><![CDATA[Power Platform Developer]]></category><category><![CDATA[power platform development services,]]></category><category><![CDATA[Dataverse]]></category><category><![CDATA[Microsoft Dataverse]]></category><category><![CDATA[Dataverse Plugins]]></category><category><![CDATA[Dataverse Plug-in]]></category><category><![CDATA[Dataverse Solutions]]></category><category><![CDATA[#dataverse, #dynamics365, #learning #dynamics365fo]]></category><category><![CDATA[PluginAssembly]]></category><category><![CDATA[Web API]]></category><category><![CDATA[WebAPI]]></category><category><![CDATA[web apis]]></category><category><![CDATA[web api developer]]></category><category><![CDATA[Power Apps Development]]></category><category><![CDATA[power app development ]]></category><category><![CDATA[custom power apps development]]></category><category><![CDATA[Microsoft PowerApps development]]></category><category><![CDATA[ms crm]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Wed, 22 Apr 2026 15:49:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/5a6b5ba6-ff13-471e-93f2-ebe14f55d552.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post, we’ll look at how to query the <strong>Plugin Assembly</strong> entity directly to retrieve the creator and modifier details within <strong>Dynamics 365 CE / Power Platform</strong> using a simple GET request.</p>
<hr />
<h2>The Challenge</h2>
<p>The standard <strong>Power Apps Maker Portal</strong> and the classic <strong>Dynamics 365 CE</strong> interface often show when a solution was modified, but finding the specific metadata for a single assembly—specifically the <strong>Modified By</strong> and <strong>Created By</strong> system user details—can be a hassle.</p>
<h2>The Solution: Using the Dataverse Web API</h2>
<p>Every plugin assembly in <strong>Dynamics 365 CE / Power Platform</strong> is stored in the <code>pluginassemblies</code> table. By targeting the specific GUID of your assembly, you can expand the record to include details from the <code>systemuser</code> table.</p>
<h3>The API Query</h3>
<p>Replace <code>&lt;your-org.crm.dynamics.com&gt;</code> with your environment URL and <code>&lt;PLUGIN_GUID&gt;</code> with the unique ID of the assembly you are investigating.</p>
<p>HTTP</p>
<pre><code class="language-graphql">GET https://&lt;your-org.crm.dynamics.com&gt;/api/data/v9.2/pluginassemblies(&lt;PLUGIN_GUID&gt;)?$select=name,modifiedon,createdon
&amp;\(expand=modifiedby(\)select=fullname,internalemailaddress),createdby($select=fullname,internalemailaddress)
</code></pre>
<h3>Breakdown of the Query:</h3>
<ul>
<li><p><code>$select=name,modifiedon,createdon</code>: Narrows the results to the assembly name and the specific timestamps.</p>
</li>
<li><p><code>$expand</code>: The Web API equivalent of a "Join," allowing us to reach out to the linked <code>systemuser</code> table.</p>
</li>
<li><p><code>modifiedby(...)</code>: Retrieves the Full Name and Email of the user who last updated the assembly.</p>
</li>
<li><p><code>createdby(...)</code>: Retrieves the Full Name and Email of the user who has created the assembly.</p>
</li>
</ul>
<hr />
<h2>Understanding the Output</h2>
<p>When you run this query in your browser or a tool like Postman, you will receive a JSON response. This provides clear visibility into which account performed the deployment.</p>
<p><strong>Example Response:</strong></p>
<p>JSON</p>
<pre><code class="language-json">{
  "@odata.context": "https://&lt;your-org&gt;.crm.dynamics.com/api/data/v9.2/\(metadata#pluginassemblies(name,modifiedon,createdon,modifiedby(fullname,internalemailaddress),createdby(fullname,internalemailaddress))/\)entity",
  "name": "XRM.ABC.CRM.Plugins.Core",
  "createdon": "2026-01-22T01:32:54Z",
  "pluginassemblyid": "5e96d04e-5435-48c0-9c01-637a210620cd",
  "modifiedon": "2026-04-22T14:22:15Z",
  "modifiedby": {
    "internalemailaddress": "AProcess@xyz.com",
    "fullname": "Auto Process"
  },
  "createdby": {
    "internalemailaddress": "AProcess@xyz.com",
    "fullname": "Auto Process"
  }
}
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/769cf2ea-db23-4637-a820-58488eb76cc6.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>How to Find the Plugin GUID</h2>
<p>If you don't have the GUID handy, you can find it using these common <strong>Dynamics 365 CE / Power Platform</strong> tools:</p>
<ol>
<li><p><strong>XrmToolBox</strong>: Use the <em>Plugin Registration Tool</em> or <em>Metadata Browser</em>.</p>
</li>
<li><p><strong>Web API</strong>: Run a general query to list all assemblies: <code>GET https://&lt;your-org.crm.dynamics.com&gt;/api/data/v9.2/pluginassemblies?$select=name,pluginassemblyid</code></p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/95020b11-7306-4e21-bfb2-840228a2d3b5.png" alt="" style="display:block;margin:0 auto" />

<h2>Why This Matters</h2>
<ul>
<li><p><strong>Accountability:</strong> Easily identify which developer or service account (like "Auto Process") deployed the code.</p>
</li>
<li><p><strong>Troubleshooting:</strong> Compare the <code>modifiedon</code> date with the time a bug was first reported.</p>
</li>
<li><p><strong>Environment Auditing:</strong> Ensure that deployments across your <strong>Dynamics 365 CE and Power Platform</strong> instances follow your team's governance policies.</p>
</li>
</ul>
<h2>Pro-Tip: The Browser Shortcut</h2>
<p>You don’t need a specialized tool to run this. As long as you are logged into your <strong>Dynamics 365 CE / Power Platform</strong> environment, you can paste the API URL directly into a new browser tab. The browser uses your authenticated session to return the data immediately!</p>
]]></content:encoded></item><item><title><![CDATA[How to Block Auto Save in Microsoft Dynamics 365 CRM / Dynamics CE / PowerApps Using JavaScript]]></title><description><![CDATA[Auto Save in Microsoft Dynamics 365 Customer Engagement (CE) can be useful, but there are many business cases where it becomes a problem.
You may need to block auto save when:

A Canvas App or dialog ]]></description><link>https://blog.cleverwizard.com/how-to-block-auto-save-in-microsoft-dynamics-365-crm-dynamics-ce-powerapps-using-javascript</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-block-auto-save-in-microsoft-dynamics-365-crm-dynamics-ce-powerapps-using-javascript</guid><category><![CDATA[dynamics]]></category><category><![CDATA[Dynamics 365]]></category><category><![CDATA[dynamics 365 crm]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[autosave]]></category><category><![CDATA[Dynamics CE]]></category><category><![CDATA[dynamics crm]]></category><category><![CDATA[Dynamics CRM Partner ]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Thu, 11 Dec 2025 12:46:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/9b8d89e0-29fd-43a2-912a-4d433e8e867e.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Auto Save in Microsoft Dynamics 365 Customer Engagement (CE) can be useful, but there are many business cases where it becomes a problem.</p>
<p>You may need to block auto save when:</p>
<ul>
<li><p>A Canvas App or dialog is collecting additional data</p>
</li>
<li><p>Auto-save triggers Power Automate flows too early</p>
</li>
<li><p>Required fields are not yet filled</p>
</li>
<li><p>You want to force users to save manually</p>
</li>
<li><p>Background saves cause inconsistent or incomplete updates</p>
</li>
</ul>
<p>In such situations, blocking auto save across the form—or only under specific conditions—can prevent unwanted logic from running prematurely.</p>
<h2><strong>Why Auto Save Can Cause Problems</strong></h2>
<p>Dynamics CE automatically performs a save every 30 seconds and during certain background operations.<br />This can cause issues like:</p>
<ul>
<li><p>Power Automate flows firing without required data</p>
</li>
<li><p>Incomplete values (like file names) passed to plugins</p>
</li>
<li><p>Business rules executing too early</p>
</li>
<li><p>Canvas App dialogs submitting half-filled records</p>
</li>
</ul>
<p>To avoid these issues, you can stop auto-save using JavaScript.</p>
<h1><strong>JavaScript Code to Block Auto Save</strong></h1>
<p>Here is the core script:</p>
<pre><code class="language-javascript">function blockAutoSave(eContext) {
    var saveEvent = eContext.getEventArgs();
    if (saveEvent.getSaveMode() === 70 || saveEvent.getSaveMode() === 2) {
        // autosave or background save
        saveEvent.preventDefault();
    }
}
</code></pre>
<h1><strong>Three Ways to Use This Script</strong></h1>
<p>Dynamics CE gives you complete flexibility depending on your scenario.</p>
<h2><strong>Option 1: Block Auto Save Permanently for the Form</strong></h2>
<p>Attach only the function <code>blockAutoSave</code> to the form’s <strong>On Save</strong> event.</p>
<p>This will:</p>
<ul>
<li><p>Block auto-save for the entire form</p>
</li>
<li><p>Allow manual Save to work normally</p>
</li>
</ul>
<p><strong>Steps:</strong></p>
<ol>
<li><p>Open the Form Editor</p>
</li>
<li><p>Go to <strong>Form Properties</strong></p>
</li>
<li><p>Add your JS Web Resource</p>
</li>
<li><p>Add event handler under "On Save":</p>
<ul>
<li><p>Function name: <code>blockAutoSave</code></p>
</li>
<li><p>Check <strong>Pass execution context</strong></p>
</li>
</ul>
</li>
<li><p>Publish</p>
</li>
</ol>
<p>This option is ideal when an entity should <strong>never</strong> auto-save (e.g., Cases, custom entities with complex logic).</p>
<h2><strong>Option 2: Block Auto Save Only in Specific Scenarios</strong></h2>
<p>You can call:</p>
<pre><code class="language-javascript">formContext.data.entity.addOnSave(blockAutoSave);
</code></pre>
<p>This allows you to block auto save only when needed, for example:</p>
<ul>
<li><p>When your Canvas App dialog is open</p>
</li>
<li><p>When required fields are empty</p>
</li>
<li><p>When a specific flag or checkbox is true</p>
</li>
<li><p>When the user executes a ribbon button action</p>
</li>
</ul>
<p>This gives you more control because you decide <strong>when auto save should be blocked</strong>.</p>
<h2><strong>Option 3: Dynamically Add or Remove the Auto-Save Blocker</strong></h2>
<p>You can also enable or disable the blocker during runtime based on conditions.</p>
<h3><strong>Add the blocker</strong></h3>
<pre><code class="language-javascript">formContext.data.entity.addOnSave(blockAutoSave);
</code></pre>
<h3><strong>Remove the blocker</strong></h3>
<pre><code class="language-javascript">formContext.data.entity.removeOnSave(blockAutoSave);
</code></pre>
<h3>⚠ Important</h3>
<p>The function reference must be <strong>exactly the same</strong>.<br />This works:</p>
<pre><code class="language-javascript">formContext.data.entity.removeOnSave(blockAutoSave);
</code></pre>
<p>This does NOT work:</p>
<pre><code class="language-javascript">formContext.data.entity.removeOnSave(function(e){ ... }); // new function reference
</code></pre>
<h2><strong>Example: Enable or Disable Auto-Save Based on a Field Value</strong></h2>
<pre><code class="language-javascript">function onLoad(eContext) {
    var formContext = eContext.getFormContext();
    var isBlockingEnabled = formContext.getAttribute("new_enableblock").getValue();

    if (isBlockingEnabled === true) {
        // Block auto-save
        formContext.data.entity.addOnSave(blockAutoSave);
    } else {
        // Allow auto-save
        formContext.data.entity.removeOnSave(blockAutoSave);
    }
}

function blockAutoSave(eContext) {
    var saveEvent = eContext.getEventArgs();
    if (saveEvent.getSaveMode() === 70 || saveEvent.getSaveMode() === 2) {
        saveEvent.preventDefault();
    }
}
</code></pre>
<p>This is extremely useful when you want fine-grained control.</p>
<h1><strong>SaveMode Values Used in This Script</strong></h1>
<p>Dynamics 365 CE uses different <strong>Save Modes</strong> to indicate <em>why</em> a save was triggered. When your JavaScript OnSave event handler executes, you can inspect the Save Mode to decide what should happen.</p>
<p>Below are the relevant codes used when blocking auto save:</p>
<table>
<thead>
<tr>
<th><strong>Save Mode</strong></th>
<th><strong>Name</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>2</strong></td>
<td>Auto Save</td>
<td>Triggered every 30 seconds or when Dynamics performs its default auto-save behavior. This is the most common unwanted save, often happening before required fields are populated or before the user is ready.</td>
</tr>
<tr>
<td><strong>70</strong></td>
<td>Background Save</td>
<td>A system-triggered save that happens during operations such as navigating away from a form, closing a record, switching tabs, or performing actions that require the form data to be committed. This save often behaves similar to auto save but is not user-initiated.</td>
</tr>
</tbody></table>
<h3><strong>Why These Two Save Modes Are Blocked</strong></h3>
<p>These two modes—<strong>2 (Auto Save)</strong> and <strong>70 (Background Save)</strong>—are the ones that Dynamics triggers <em>without the user manually clicking Save, Save &amp; Close, or Save &amp; New</em>.</p>
<p>Blocking them ensures that:</p>
<ul>
<li><p>Unfinished or partial data does <strong>not</strong> get saved</p>
</li>
<li><p>Power Automate or plugin logic is <strong>not triggered prematurely</strong></p>
</li>
<li><p>Canvas App dialogs or additional UI components can work safely without the system trying to commit changes</p>
</li>
<li><p>Users maintain control of <em>when</em> the record is saved</p>
</li>
</ul>
<h3><strong>What Is <em>Not</em> Blocked</strong></h3>
<p>Your script does <strong>not</strong> block:</p>
<ul>
<li><p>Manual <strong>Save</strong> (Save Mode 1)</p>
</li>
<li><p><strong>Save &amp; Close</strong></p>
</li>
<li><p><strong>Save &amp; New</strong></p>
</li>
<li><p>Script-triggered save operations (unless you explicitly check for them)</p>
</li>
</ul>
<p>This ensures that users can still save the form normally—only auto or background saves are prevented.</p>
<p>By filtering only on Save Modes <strong>2</strong> and <strong>70</strong>, the script provides protection from unwanted auto-saves while still allowing users to save intentionally.<br />This approach keeps the system predictable, prevents accidental triggers, and gives full control back to the user or your custom logic.</p>
<h1><strong>Step-by-Step Setup</strong></h1>
<h3><strong>1. Create a JavaScript Web Resource</strong></h3>
<ul>
<li><p>Go to <strong>Advanced Settings → Customizations → Solutions</strong></p>
</li>
<li><p>Add a new <strong>JS Web Resource</strong></p>
</li>
<li><p>Paste the script</p>
</li>
<li><p>Save and publish</p>
</li>
</ul>
<h3><strong>2. Add the Script to Your Form</strong></h3>
<p>Depending on which option you choose, attach:</p>
<ul>
<li><p>Only the function (<code>blockAutoSave</code>)</p>
</li>
<li><p>Or call <code>addOnSave()</code> dynamically</p>
</li>
<li><p>Or add/remove the event based on conditions</p>
</li>
</ul>
<h1><strong>Conclusion</strong></h1>
<p>With a small amount of JavaScript, you gain full control over how data is saved in Microsoft Dynamics 365 CE.</p>
<p>You now have three powerful techniques:</p>
<ol>
<li><p><strong>Block auto save permanently</strong> for the entire form</p>
</li>
<li><p><strong>Block auto save only in specific scenarios</strong></p>
</li>
<li><p><strong>Add or remove the auto-save blocker dynamically</strong>, based on real-time conditions</p>
</li>
</ol>
<p>This prevents accidental triggers, incomplete data saves, and unwanted flows—keeping your system stable and predictable.</p>
]]></content:encoded></item><item><title><![CDATA[How to Retrieve Knowledge Article Attachments (msdyn_kbattachment) Using FetchXML]]></title><description><![CDATA[In Dynamics 365, knowledge articles often come with file attachments (such as PDFs, images, or documents) stored in the msdyn_kbattachment entity. If you want to retrieve these attachments programmati]]></description><link>https://blog.cleverwizard.com/how-to-retrieve-knowledge-article-attachments-msdynkbattachment-using-fetchxml</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-retrieve-knowledge-article-attachments-msdynkbattachment-using-fetchxml</guid><category><![CDATA[fetch]]></category><category><![CDATA[Web API]]></category><category><![CDATA[Power Platform]]></category><category><![CDATA[Power Platform Solutions]]></category><category><![CDATA[Dynamics CE]]></category><category><![CDATA[dynamics crm]]></category><category><![CDATA[Query]]></category><category><![CDATA[knowledge]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[fetch API]]></category><category><![CDATA[fetchxml  Dynamics 365  PowerPlatform  Query]]></category><category><![CDATA[Dynamics 365]]></category><category><![CDATA[dynamics 365 crm]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Fri, 26 Sep 2025 12:00:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/777dba82-695c-437a-87ce-26818fcdfc41.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Dynamics 365, knowledge articles often come with file attachments (such as PDFs, images, or documents) stored in the <strong>msdyn_kbattachment</strong> entity. If you want to retrieve these attachments programmatically, you can use <strong>FetchXML</strong> to query the related records and then make an HTTP request to download the attachment in base64 format.</p>
<p>This blog will walk you through the process step by step.</p>
<p>Lets say you have a knowledge article with 3 attachments:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758887476311/4357b197-7838-4bf4-addd-b9d8cd87d639.png" alt="" style="display:block;margin:0 auto" />

<h2>Step 1: Get the Knowledge Article ID</h2>
<p>Before you can retrieve attachments, you need the <strong>Knowledge Article ID</strong> (<code>knowledgearticleid</code>) of the article that has attachments. This ID will be passed into the FetchXML query.</p>
<p>You can extract the knowledge article id from the URL:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758887533226/0bc3f307-5d57-4670-a1ae-769cf8f3ad7b.png" alt="" style="display:block;margin:0 auto" />

<p>Example:</p>
<pre><code class="language-plaintext">86284168-ac72-4f7d-ae1b-7e1e68b0c8ca
</code></pre>
<h2>Step 2: Build the FetchXML Query</h2>
<p>Use the following FetchXML to retrieve attachments for a given knowledge article. Replace the <code>knowledgearticleid</code> value with your own.</p>
<pre><code class="language-xml">&lt;fetch version="1.0" output-format="xml-platform" mapping="logical" returntotalrecordcount="true" no-lock="false"&gt;
  &lt;entity name="msdyn_kbattachment"&gt;
    &lt;attribute name="msdyn_fileicon_url" /&gt;
    &lt;attribute name="statecode" /&gt;
    &lt;attribute name="msdyn_filename" /&gt;
    &lt;attribute name="msdyn_filesize" /&gt;
    &lt;attribute name="msdyn_kbattachmentid" /&gt;
    &lt;attribute name="msdyn_filetype" /&gt;
    &lt;order attribute="msdyn_filename" descending="false" /&gt;
    &lt;link-entity name="msdyn_msdyn_kbattachment_knowledgearticle" intersect="true" visible="false" to="msdyn_kbattachmentid" from="msdyn_kbattachmentid"&gt;
      &lt;link-entity name="knowledgearticle" from="knowledgearticleid" to="knowledgearticleid" alias="ka"&gt;
        &lt;filter type="and"&gt;
          &lt;condition attribute="knowledgearticleid" operator="eq" uitype="knowledgearticle" value="86284168-ac72-4f7d-ae1b-7e1e68b0c8ca" /&gt;
        &lt;/filter&gt;
      &lt;/link-entity&gt;
    &lt;/link-entity&gt;
  &lt;/entity&gt;
&lt;/fetch&gt;
</code></pre>
<p>This query will return the following details for each attachment:</p>
<ul>
<li><p><strong>File Name</strong> (<code>msdyn_filename</code>)</p>
</li>
<li><p><strong>File Size</strong> (<code>msdyn_filesize</code>)</p>
</li>
<li><p><strong>File Type</strong> (<code>msdyn_filetype</code>)</p>
</li>
<li><p><strong>Attachment ID</strong> (<code>msdyn_kbattachmentid</code>)</p>
</li>
<li><p><strong>File Icon URL</strong> (<code>msdyn_fileicon_url</code>)</p>
</li>
<li><p><strong>State Code</strong> (<code>statecode</code>)</p>
</li>
</ul>
<p>Response:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758887619437/7edba861-cd44-434a-a239-d21e11f6e3b1.png" alt="" style="display:block;margin:0 auto" />

<h2>Step 3: Download the Attachment Content</h2>
<p>Once you have the <code>msdyn_kbattachmentid</code> from the FetchXML results, you can retrieve the actual file content using a Web API HTTP GET request.</p>
<p>Example request:</p>
<pre><code class="language-http">GET https://&lt;your_org&gt;.api.crm4.dynamics.com/api/data/v9.2/msdyn_kbattachments(&lt;msdyn_kbattachmentid&gt;)/msdyn_fileattachment
</code></pre>
<p>For example:</p>
<pre><code class="language-http">GET https://yourorg.api.crm4.dynamics.com/api/data/v9.2/msdyn_kbattachments(2b76e2ec-ce9a-f011-b4cc-000d3adead23)/msdyn_fileattachment
</code></pre>
<p>This will return a response a <strong>JSON</strong> with the <strong>base64-encoded file content</strong>.</p>
<pre><code class="language-json">{
"@odata.context": "https://yourorg.api.crm4.dynamics.com/api/data/v9.2/$metadata#msdyn_kbattachments(2b76e2ec-ce9a-f011-b4cc-000d3adead23)/msdyn_fileattachment",
"value": "JVBERi0xLjQKJeLjz9MKNCAwIG9iago8PC9UeXBlL..."
}
</code></pre>
<h2>Step 4: Parse the JSON and Decode the Base64 Content</h2>
<p>The HTTP GET response will return JSON with the <code>value</code> property containing the base64-encoded file.</p>
<p>Example:</p>
<pre><code class="language-json">{
  "@odata.context": "https://&lt;org&gt;.api.crm4.dynamics.com/api/data/v9.2/$metadata#msdyn_kbattachments/msdyn_fileattachment",
  "value": "JVBERi0xLjQKJeLjz9MKNCAwIG9iago8PC9UeXBlL..."
}
</code></pre>
<p>To save the file locally in C#:</p>
<pre><code class="language-json">// Extract base64 string from JSON response
string base64FileContent = jsonResponse["value"].ToString();

// Decode and save
byte[] fileBytes = Convert.FromBase64String(base64FileContent);
File.WriteAllBytes(@"C:\\Downloads\\Attachment.pdf", fileBytes);
</code></pre>
<hr />
<h2>Summary</h2>
<ul>
<li><p>Use <strong>FetchXML</strong> to retrieve attachment metadata related to a knowledge article.</p>
</li>
<li><p>Get the <strong>Attachment ID</strong> (<code>msdyn_kbattachmentid</code>).</p>
</li>
<li><p>Call the <strong>Web API GET endpoint</strong> <code>/msdyn_fileattachment</code> to fetch the base64 content.</p>
</li>
<li><p>Parse the JSON and Decode the base64 string and save the file locally.</p>
</li>
</ul>
<p>This approach helps you programmatically fetch and download knowledge article attachments stored in Dynamics 365.</p>
]]></content:encoded></item><item><title><![CDATA[How to Fetch Available Environments from Dynamics 365 CRM]]></title><description><![CDATA[While working with Microsoft Dynamics 365 and the Power Platform, having visibility into all available environments is crucial. This information comes in handy for automating processes, managing integ]]></description><link>https://blog.cleverwizard.com/how-to-fetch-available-environments-from-dynamics-365-crm</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-fetch-available-environments-from-dynamics-365-crm</guid><category><![CDATA[Web API]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[Dynamics 365]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[Dataverse]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Tue, 16 Sep 2025 12:08:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/db87bfa5-0a0d-43a0-87f7-e2325d150259.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While working with Microsoft Dynamics 365 and the Power Platform, having visibility into all available environments is crucial. This information comes in handy for automating processes, managing integrations, and handling administrative tasks. In this guide, we’ll explore how to retrieve environment details using the Web API.</p>
<h2>Prerequisites</h2>
<p>Before you start, make sure you have:</p>
<ul>
<li><p>An app registered in <strong>Azure Active Directory</strong></p>
</li>
<li><p>The proper <strong>API permissions</strong> for Dynamics CRM</p>
</li>
<li><p>Your <strong>Client ID</strong> and <strong>Client Secret</strong></p>
</li>
</ul>
<h2>Step 1: Get an Access Token</h2>
<p>You first need to authenticate with Azure AD. Send a <code>POST</code> request to the token endpoint:</p>
<pre><code class="language-apache">POST: https://login.microsoftonline.com/{tenant_id}/oauth2/token
</code></pre>
<p><strong>Headers:</strong></p>
<pre><code class="language-javascript">Content-Type: application/x-www-form-urlencoded
</code></pre>
<p><strong>Body:</strong></p>
<pre><code class="language-apache">grant_type=client_credentials
client_id={your_client_id}
client_secret={your_client_secret}
resource=https://yourorg.crm.dynamics.com/
</code></pre>
<p>This will return an <strong>access token</strong> you can use to call the API.</p>
<h2>Step 2: Fetch Environments</h2>
<p>With the token, send a <code>GET</code> request to the Power Platform environments API:</p>
<pre><code class="language-apache">GET https://api.powerplatform.com/providers/Microsoft.BusinessAppPlatform/environments?api-version=2020-10-01
</code></pre>
<p><strong>Headers:</strong></p>
<pre><code class="language-javascript">Authorization: Bearer {access_token}
Content-Type: application/json
</code></pre>
<h2>Step 3: Review the Response</h2>
<p>The response will include all available environments with details like:</p>
<pre><code class="language-javascript">{
  "value": [
    {
      "id": "12345",
      "name": "Default",
      "location": "North America",
      "state": "Ready"
    },
    {
      "id": "67890",
      "name": "Sandbox",
      "location": "Europe",
      "state": "Ready"
    }
  ]
}
</code></pre>
<h2>Step 4: Use the Data</h2>
<p>Now that you have the list, you can use it in your application—for example, to let users pick which environment they want to work with.</p>
<h2>Conclusion</h2>
<p>Fetching available environments is a simple but powerful step when working with Dynamics 365 CRM. It allows you to dynamically manage solutions, automate processes, and build more flexible applications.</p>
]]></content:encoded></item><item><title><![CDATA[Enhancing Dynamics 365 CE /PowerApps Fetch XML with OR Conditions between Linked Entities]]></title><description><![CDATA[FetchXML is a powerful query language used in Dynamics 365 Customer Engagement (CE) and PowerApps for retrieving data. One of the common requirements is to add complex filtering conditions, such as us]]></description><link>https://blog.cleverwizard.com/enhancing-dynamics-365-ce-powerapps-fetch-xml-with-or-conditions-between-linked-entities</link><guid isPermaLink="true">https://blog.cleverwizard.com/enhancing-dynamics-365-ce-powerapps-fetch-xml-with-or-conditions-between-linked-entities</guid><category><![CDATA[fetchxml  Dynamics 365  PowerPlatform  Query]]></category><category><![CDATA[fetchxml]]></category><category><![CDATA[Dynamics 365]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[Query]]></category><category><![CDATA[Dynamics CE]]></category><category><![CDATA[dynamics crm]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Thu, 11 Sep 2025 08:15:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/94e91b23-38da-461e-9aa6-f028f046231c.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>FetchXML is a powerful query language used in Dynamics 365 Customer Engagement (CE) and PowerApps for retrieving data. One of the common requirements is to add complex filtering conditions, such as using <code>OR</code> conditions in linked entities. In this blog post, we'll explore how to modify a FetchXML query to include <code>OR</code> conditions in linked entities, making your data retrieval more flexible and efficient.</p>
<h2><strong>Understanding the Scenario</strong></h2>
<p>Consider a scenario where we need to fetch activities from Dynamics 365 CE and PowerApps and apply filters on related entities. Specifically, we want to filter activities based on a group attribute in two linked entities: <code>contact</code> and <code>account</code>. We need to use an <code>OR</code> condition to match the group attribute in either of the linked entities.</p>
<h2><strong>The FetchXML Query</strong></h2>
<p>Here's the FetchXML query with the <code>OR</code> condition applied to the linked entities:</p>
<pre><code class="language-xml">&lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true"&gt;
    &lt;entity name="activitypointer"&gt;
        &lt;attribute name="activitytypecode" /&gt;
        &lt;attribute name="subject" /&gt;
        &lt;attribute name="statecode" /&gt;
        &lt;attribute name="prioritycode" /&gt;
        &lt;attribute name="modifiedon" /&gt;
        &lt;attribute name="activityid" /&gt;
        &lt;attribute name="instancetypecode" /&gt;
        &lt;attribute name="community" /&gt;
        &lt;attribute name="regardingobjectid" /&gt;
        &lt;order attribute="modifiedon" descending="false" /&gt;
        &lt;link-entity name="contact" from="contactid" to="regardingobjectid" link-type="outer" alias="an"&gt;
            &lt;link-entity name="xrm_borrowercustomnn" from="xrm_borrower" to="contactid" link-type="outer" alias="ao" /&gt;
        &lt;/link-entity&gt;
        &lt;link-entity name="account" from="accountid" to="regardingobjectid" link-type="outer" alias="ap"&gt;
            &lt;link-entity name="xrm_borrowercustomnn" from="xrm_borrower" to="accountid" link-type="outer" alias="aq" /&gt;
        &lt;/link-entity&gt;
        &lt;filter type="or"&gt;
            &lt;condition entityname="ao" attribute="xrm_group" operator="eq" value="{C907238B-1B14-EF11-9F89-000D3AF2E8B8}" /&gt;
            &lt;condition entityname="aq" attribute="xrm_group" operator="eq" value="{C907238B-1B14-EF11-9F89-000D3AF2E8B8}" /&gt;
        &lt;/filter&gt;
    &lt;/entity&gt;
&lt;/fetch&gt;
</code></pre>
<h2><strong>Explanation of the FetchXML Query</strong></h2>
<p>Let's break down the key parts of this FetchXML query:</p>
<ul>
<li><p><strong>Entity:</strong> We are querying the <code>activitypointer</code> entity to fetch various activity attributes such as <code>subject</code>, <code>statecode</code>, <code>prioritycode</code>, and <code>modifiedon</code>.</p>
</li>
<li><p><strong>Linked Entities:</strong> We are linking to the <code>contact</code> and <code>account</code> entities using outer joins. These linked entities have a further link to a custom entity <code>xrm_borrowercustomnn</code>.</p>
</li>
<li><p><strong>Filter Condition:</strong> The <code>filter</code> element is of type <code>or</code>, meaning it will match any of the conditions inside it. The conditions check if the <code>xrm_group</code> attribute in either the <code>contact</code> or <code>account</code> entity matches the specified value.</p>
</li>
</ul>
<h2><strong>Practical Usage</strong></h2>
<p>This FetchXML query can be used in various scenarios where complex filtering is required. For example, you might want to display activities related to specific groups in a dashboard or report. By using the <code>OR</code> condition, you ensure that activities linked to either a <code>contact</code> or <code>account</code> with the specified group are included in the results.</p>
<h2><strong>Conclusion</strong></h2>
<p>Adding <code>OR</code> conditions in linked entities within FetchXML queries can significantly enhance the flexibility of your data retrieval in Dynamics 365 CE and PowerApps. By understanding and applying these techniques, you can create more powerful and efficient queries tailored to your business needs.</p>
<p>We hope this guide has been helpful. If you have any questions or need further assistance, feel free to leave a comment below.</p>
]]></content:encoded></item><item><title><![CDATA[How to Download SharePoint Recycle Bin as a CSV (No PowerShell Needed)]]></title><description><![CDATA[Auditing the SharePoint Recycle Bin is difficult without PowerShell or administrative permissions. This guide offers a simple, non-destructive method to download a complete list of deleted items as a ]]></description><link>https://blog.cleverwizard.com/how-to-download-sharepoint-recycle-bin-as-a-csv-no-powershell-needed</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-download-sharepoint-recycle-bin-as-a-csv-no-powershell-needed</guid><category><![CDATA[SharePoint]]></category><category><![CDATA[SharePoint Online]]></category><category><![CDATA[APIs]]></category><category><![CDATA[tricks]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[recovery]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[SharePointFramework]]></category><category><![CDATA[sharepoint consulting companies]]></category><category><![CDATA[coding]]></category><category><![CDATA[CSV files]]></category><category><![CDATA[csv]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Tue, 09 Sep 2025 19:38:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/6eed2b08-25fb-4d8c-929e-5473c0255234.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Auditing the SharePoint Recycle Bin is difficult without PowerShell or administrative permissions. This guide offers a simple, non-destructive method to download a complete list of deleted items as a CSV, using just a small JavaScript code snippet in your browser's developer console.</p>
<p>This method is perfect for site owners or users with "Read" permissions who need a quick report of what's been deleted.</p>
<h2>What You'll Need</h2>
<ul>
<li><p><strong>A web browser:</strong> Chrome, Edge, or Firefox.</p>
</li>
<li><p><strong>Access to your SharePoint site:</strong> You must be logged in with sufficient permissions to view the Recycle Bin.</p>
</li>
<li><p><strong>The JavaScript code snippet:</strong> Provided below.</p>
</li>
</ul>
<h2>Step 1: Navigate to the SharePoint Recycle Bin</h2>
<p>First, open your web browser and go to your SharePoint site's <strong>Recycle Bin</strong>. You can usually find this by going to <strong>Site Contents</strong> and clicking <strong>Recycle Bin</strong> at the top right of the page.</p>
<p>Your URL will look something like this: <a href="https://yourdomain.sharepoint.com/sites/YourSite/_layouts/15/RecycleBin.aspx"><code>https://yourdomain.sharepoint.com/sites/&lt;YourSite&gt;/_layouts/15/RecycleBin.aspx</code></a></p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757446109735/7a53e782-7481-4876-af06-8b4631fe4f36.jpeg" alt="" style="display:block;margin:0 auto" />

<h2>Step 2: Open the Developer Console (F12)</h2>
<p>Now, we need to access the browser's developer tools. This is where we'll paste and run our code.</p>
<ul>
<li><p>On Windows, press <strong>F12</strong> or <strong>Ctrl + Shift + I</strong>.</p>
</li>
<li><p>On macOS, press <strong>Cmd + Option + I</strong>.</p>
</li>
</ul>
<p>This will open the Developer Tools panel. Click on the <strong>Console</strong> tab.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757446168589/17b96b3e-d847-4170-93b9-a5e268533988.png" alt="" style="display:block;margin:0 auto" />

<h2>Step 3: Enable Paste in the Console</h2>
<p>For security reasons, some browsers (like Chrome and Edge) will prevent you from pasting code directly into the console. You'll see a warning message like "Do not paste code here unless you understand what you're doing."</p>
<p>To bypass this, you'll need to type <code>allow pasting</code> or a similar command into the console and hit Enter. Follow the specific instructions provided in the warning message to enable pasting.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757446345582/8f3da492-3990-400c-9c9e-e59a33edd1fc.png" alt="" style="display:block;margin:0 auto" />

<h2>Step 4: Paste and Customize the Code</h2>
<p>Copy the following JavaScript code snippet. This script is designed to page through the SharePoint API, retrieve all items from the Recycle Bin, and then automatically compile and download a CSV file.</p>
<pre><code class="language-javascript">async function getAllRecycleBinItems() {
  // ⚙️ USER CONFIGURATION: Update these variables for your specific needs
  const siteUrl = "https://yourOrg.sharepoint.com/sites/accounting"; // Your SharePoint site URL
  const rowLimit = 5000; // Number of items to fetch per API call (max 5000)
  const itemState = 1; // 1 for first-stage recycle bin, 2 for second-stage

  // 🤖 DO NOT EDIT THE CODE BELOW THIS LINE
  let all = []; // Array to store all collected Recycle Bin items
  let keepOnRunning = true;
  let lastRecordId = null; // Used for paging: ID of the last item in the previous result set
  let lastRecordTitle = null; // Used for paging: Title of the last item
  let lastRecordDeletedDate = null; // Used for paging: DeletedDate of the last item
  let currentPage = 1; // Counter for logging progress

  try {
    // Construct URL for the first page fetch
    let url = `\({siteUrl}/_api/web/GetRecycleBinItemsByQueryInfo(rowLimit=@a1,isAscending=@a2,itemState=@a3,orderby=@a4,ShowOnlyMyItems=@a5)?@a1='\){rowLimit}'&amp;@a2=false&amp;@a3=${itemState}&amp;@a4=3&amp;@a5=false`;

    console.log(`Starting to retrieve items from the Recycle Bin at: ${siteUrl}`);
    const resp = await fetch(url, {
      headers: { "Accept": "application/json;odata=verbose" }
    });

    if (!resp.ok) {
      // Check for a non-200 HTTP status code and throw an error
      throw new Error(`HTTP error! status: ${resp.status}`);
    }

    const data = await resp.json();
    // Navigate the JSON response to find the results array
    const page = data?.d?.GetRecycleBinItemsByQueryInfo || data?.d;
    let results = page?.results || [];

    if (results.length &gt; 0) {
      // Map the results to a new array with only the required properties
      const selected = results.map(item =&gt; ({
        Title: item.Title,
        DirName: item.DirName,
        DeletedByName: item.DeletedByName,
        DeletedDate: item.DeletedDate,
        ItemType: item.ItemType,
        Size: item.Size
      }));
      all.push(...selected); // Add the mapped items to the main array
    }

    console.log(`✅ Page \({currentPage} fetched. Retrieved \){results.length} items. Total so far: ${all.length}.`);

    // Check if there are more pages to fetch
    if (results.length === rowLimit) {
      // Get the last item's details for the next page's URL
      lastRecordId = results[rowLimit - 1].Id;
      // Replace single quotes in the title to prevent URL errors
      lastRecordTitle = results[rowLimit - 1].Title.replace(/'/g, "%27");
      lastRecordDeletedDate = results[rowLimit - 1].DeletedDate.replace("Z", "");
    } else {
      keepOnRunning = false; // No more pages to fetch
    }

    // Loop to fetch all subsequent pages
    while (keepOnRunning) {
      currentPage++;
      // Encode the paging values for the URL
      const encodedId = encodeURIComponent(lastRecordId);
      const encodedTitle = encodeURIComponent(lastRecordTitle);
      const encodedDate = encodeURIComponent(lastRecordDeletedDate);
      // Construct the PagingInfo string
      const pagingInfo = `id=\({encodedId}&amp;title=\){encodedTitle}&amp;searchValue=${encodedDate}`;

      // Construct URL for subsequent pages, including the encoded PagingInfo
      let url2 = `\({siteUrl}/_api/web/GetRecycleBinItemsByQueryInfo(rowLimit=@a1,isAscending=@a2,itemState=@a3,orderby=@a4,pagingInfo=@a5,ShowOnlyMyItems=@a6)?@a1='\){rowLimit}'&amp;@a2=false&amp;@a3=\({itemState}&amp;@a4=3&amp;@a5='\){encodeURIComponent(pagingInfo)}'&amp;@a6=false`;

      try {
        const resp2 = await fetch(url2, {
          headers: { "Accept": "application/json;odata=verbose" }
        });

        if (!resp2.ok) {
          throw new Error(`HTTP error! status: ${resp2.status}`);
        }

        const data2 = await resp2.json();
        const page2 = data2?.d?.GetRecycleBinItemsByQueryInfo || data2?.d;
        results = page2?.results || [];

        if (results.length &gt; 0) {
          const selected = results.map(item =&gt; ({
            Title: item.Title,
            DirName: item.DirName,
            DeletedByName: item.DeletedByName,
            DeletedDate: item.DeletedDate,
            ItemType: item.ItemType,
            Size: item.Size
          }));
          all.push(...selected);
        }

        console.log(`✅ Page \({currentPage} fetched. Retrieved \){results.length} items. Total so far: ${all.length}.`);

        if (results.length === rowLimit) {
          lastRecordId = results[rowLimit - 1].Id;
          lastRecordTitle = results[rowLimit - 1].Title.replace(/'/g, "%27");
          lastRecordDeletedDate = results[rowLimit - 1].DeletedDate.replace("Z", "");
        } else {
          keepOnRunning = false;
        }

      } catch (error) {
        // Log the specific error for this page and stop the loop
        console.error(`❌ An error occurred during page ${currentPage} retrieval:`, error);
        keepOnRunning = false;
      }
    }

    console.log(`\n🎉 All items have been retrieved.`);
    console.log(`Final retrieved records count: ${all.length}.`);

    if (all.length &gt; 0) {
      // Prepare data for CSV
      const headers = Object.keys(all[0]).join(",");
      const rows = all.map(obj =&gt;
        // Format each value, handling commas and quotes within the data
        Object.values(obj).map(v =&gt; `"${String(v).replace(/"/g, '""')}"`).join(",")
      );
      const csv = [headers, ...rows].join("\n");

      // Trigger the browser to download the CSV file
      const blob = new Blob([csv], { type: "text/csv" });
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = "RecycleBinItems.csv";
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);

      console.log(`⬇️ CSV downloaded with ${all.length} records.`);
    }

  } catch (error) {
    // Log any errors from the initial fetch or overall process
    console.error("❌ An error occurred:", error);
    console.log("Process terminated due to an error.");
  }
}
getAllRecycleBinItems();
</code></pre>
<p>Before pasting, you must <strong>customize the</strong> <code>siteUrl</code> variable at the top of the code to match your SharePoint site. For example, if your site is at <a href="https://yourcompany.sharepoint.com/sites/accounting"><code>https://yourcompany.sharepoint.com/sites/accounting</code></a>, change the line to:</p>
<p><code>const siteUrl = "</code><a href="https://yourcompany.sharepoint.com/sites/accounting"><code>https://yourcompany.sharepoint.com/sites/accounting</code></a><code>";</code></p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757446385144/bc880529-a4bc-4353-81d3-81268ad09232.png" alt="" style="display:block;margin:0 auto" />

<h2>Step 5: Run the Code</h2>
<p>After pasting the code, press <strong>Enter</strong>.</p>
<p>The script will immediately begin to run. You will see progress messages in the console, showing how many items have been fetched on each page. The process will continue automatically, retrieving pages of up to 5,000 items at a time.</p>
<p>Once the script has fetched all the items, you will see a final success message in the console.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757446414962/ba96306f-a64c-4a63-a9f5-e4fdb7237889.png" alt="" style="display:block;margin:0 auto" />

<h2>Step 6: Download the CSV File</h2>
<p>After the final message appears, your browser will automatically prompt you to <strong>save a file</strong> named <code>RecycleBinItems.csv</code>.</p>
<p>If your browser blocks the download, you may need to check your browser's download settings or a pop-up blocker. The console will also log a message, <code>⬇️ CSV downloaded...</code>, confirming the action.</p>
<p>Open the downloaded file in a spreadsheet program like Microsoft Excel or Google Sheets to view all your SharePoint Recycle Bin items, including their titles, deletion dates, and who deleted them.</p>
<p>That's it! You've successfully exported your SharePoint Recycle Bin contents without needing any administrative tools or special software.</p>
<h2>Conclusion</h2>
<p>By using this straightforward JavaScript approach, you can efficiently bypass the typical limitations of SharePoint's user interface, which often hides important data behind multiple clicks and lacks an export function. This method empowers you to quickly generate an auditable record of all deleted files and items, providing valuable insights into a site's history and helping you manage its content more effectively. This technique demonstrates how a few lines of code can solve a common problem, offering a powerful alternative to complex administrative tools and saving you time.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Microsoft Dynamics 365 CE (CRM) Plugins: Context, Target, and IOrganizationService]]></title><description><![CDATA[In this guide, we’ll explore:

How to use IPluginExecutionContext to access context and target entity.

How to use IOrganizationService for CRUD operations.

How to run queries using QueryExpression a]]></description><link>https://blog.cleverwizard.com/mastering-microsoft-dynamics-365-ce-crm-plugins-context-target-and-iorganizationservice</link><guid isPermaLink="true">https://blog.cleverwizard.com/mastering-microsoft-dynamics-365-ce-crm-plugins-context-target-and-iorganizationservice</guid><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics 365 crm]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[plugins]]></category><category><![CDATA[#codenewbies]]></category><category><![CDATA[code]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[powerapps]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Tue, 09 Sep 2025 09:39:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/072484dc-1f12-4296-800a-cf7a55369b39.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this guide, we’ll explore:</p>
<ul>
<li><p>How to use <strong>IPluginExecutionContext</strong> to access context and target entity.</p>
</li>
<li><p>How to use <strong>IOrganizationService</strong> for CRUD operations.</p>
</li>
<li><p>How to run queries using <strong>QueryExpression</strong> and <strong>FetchXML</strong> (with advanced examples).</p>
</li>
<li><p>How to execute special requests using the service.</p>
</li>
<li><p>Practical <strong>use cases</strong> where each technique is applied.</p>
</li>
</ul>
<p>Whether you’re starting with plugins or looking to refine your expertise, this blog will serve as a comprehensive reference.</p>
<h2>Understanding Context and Target</h2>
<p>When a plugin executes, the platform provides important runtime information through the <strong>IPluginExecutionContext</strong>. This includes details such as:</p>
<ul>
<li><p>The message (e.g., Create, Update, Delete).</p>
</li>
<li><p>The primary entity name.</p>
</li>
<li><p>The stage in the pipeline (PreValidation, PreOperation, PostOperation).</p>
</li>
<li><p>Input and output parameters.</p>
</li>
</ul>
<p>The most important input parameter is often the <strong>Target</strong>, which represents the record that triggered the event.</p>
<h3>Example: Accessing Context and Target</h3>
<pre><code class="language-javascript">public void Execute(IServiceProvider serviceProvider)
{
    IPluginExecutionContext context = (IPluginExecutionContext)
        serviceProvider.GetService(typeof(IPluginExecutionContext));

    IOrganizationServiceFactory serviceFactory =
        (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

    if (context.InputParameters.Contains("Target") &amp;&amp; context.InputParameters["Target"] is Entity entity)
    {
        string logicalName = entity.LogicalName;
        Guid id = entity.Id;

        // Example use case: Log the target entity
        tracingService.Trace($"Plugin triggered on entity: {logicalName}, Id: {id}");
    }
}
</code></pre>
<p><strong>Use case:</strong> You can use <code>context</code> and <code>Target</code> to determine whether a specific attribute was updated and conditionally run your logic. For example, only trigger follow-up actions when the <strong>status</strong> of a Case changes.</p>
<h2>Retrieve a Single Record</h2>
<pre><code class="language-javascript">Entity account = service.Retrieve("account",
    new Guid("00000000-0000-0000-0000-000000000001"),
    new ColumnSet("name", "telephone1", "primarycontactid"));

string accountName = account.GetAttributeValue&lt;string&gt;("name");
</code></pre>
<p><strong>Use case:</strong> Retrieve account details when a related contact is updated, ensuring consistent synchronization between entities.</p>
<h2>Retrieve Multiple Records (QueryExpression)</h2>
<pre><code class="language-javascript">QueryExpression query = new QueryExpression("contact")
{
    ColumnSet = new ColumnSet("fullname", "emailaddress1", "parentcustomerid")
};
query.Criteria.AddCondition("statecode", ConditionOperator.Equal, 0);
query.Criteria.AddCondition("emailaddress1", ConditionOperator.NotNull);

query.Orders.Add(new OrderExpression("fullname", OrderType.Ascending));

EntityCollection contacts = service.RetrieveMultiple(query);

foreach (var contact in contacts.Entities)
{
    string fullname = contact.GetAttributeValue&lt;string&gt;("fullname");
}
</code></pre>
<p><strong>Use case:</strong> Fetch all active contacts with valid email addresses for syncing into a mailing system.</p>
<h2>Retrieve Multiple Records (FetchXML – Advanced)</h2>
<p>FetchXML allows more complex queries that are sometimes easier to write than QueryExpression, especially when dealing with joins or aggregates.</p>
<h3>Example 1: Retrieve All Active Opportunities With Related Account Name</h3>
<pre><code class="language-javascript">string fetchXml = @"
&lt;fetch&gt;
  &lt;entity name='opportunity'&gt;
    &lt;attribute name='name' /&gt;
    &lt;attribute name='estimatedvalue' /&gt;
    &lt;filter&gt;
      &lt;condition attribute='statecode' operator='eq' value='0' /&gt;
    &lt;/filter&gt;
    &lt;link-entity name='account' from='accountid' to='customerid' alias='acc'&gt;
      &lt;attribute name='name' alias='accountname' /&gt;
    &lt;/link-entity&gt;
  &lt;/entity&gt;
&lt;/fetch&gt;";

EntityCollection opportunities = service.RetrieveMultiple(new FetchExpression(fetchXml));
</code></pre>
<p><strong>Use case:</strong> Useful when building dashboards or reports that need both opportunity and account details in one query.</p>
<h3>Example 2: Aggregate – Count Active Contacts Per Account</h3>
<pre><code class="language-javascript">string fetchXml = @"
&lt;fetch distinct='false' aggregate='true'&gt;
  &lt;entity name='contact'&gt;
    &lt;attribute name='contactid' aggregate='count' alias='contactcount' /&gt;
    &lt;link-entity name='account' from='accountid' to='parentcustomerid' alias='acc'&gt;
      &lt;attribute name='name' groupby='true' alias='accountname' /&gt;
    &lt;/link-entity&gt;
  &lt;/entity&gt;
&lt;/fetch&gt;";

EntityCollection results = service.RetrieveMultiple(new FetchExpression(fetchXml));
</code></pre>
<p><strong>Use case:</strong> Helps managers quickly see how many contacts each account has, without exporting data.</p>
<h2>Create a Record</h2>
<pre><code class="language-javascript">Entity newContact = new Entity("contact");
newContact["firstname"] = "John";
newContact["lastname"] = "Doe";
newContact["emailaddress1"] = "john.doe@example.com";

Guid contactId = service.Create(newContact);
</code></pre>
<p><strong>Use case:</strong> Automatically create a new Contact record when a lead is qualified.</p>
<h2>Update a Record</h2>
<pre><code class="language-javascript">Entity updateContact = new Entity("contact", contactId);
updateContact["telephone1"] = "123-456-7890";

service.Update(updateContact);
</code></pre>
<p><strong>Use case:</strong> Update a contact’s phone number whenever the related account’s main phone number changes.</p>
<h2>Delete a Record</h2>
<pre><code class="language-javascript">service.Delete("contact", contactId);
</code></pre>
<p><strong>Use case:</strong> Automatically clean up orphaned records when the parent entity is deleted.</p>
<h2>Execute a Special Request</h2>
<p>Dynamics 365 CRM includes specialized messages that can be executed through <code>service.Execute()</code>.</p>
<h3>Example: Win an Opportunity</h3>
<pre><code class="language-javascript">WinOpportunityRequest winRequest = new WinOpportunityRequest
{
    OpportunityClose = new Entity("opportunityclose")
    {
        ["subject"] = "Opportunity Closed as Won",
        ["opportunityid"] = new EntityReference("opportunity", opportunityId)
    },
    Status = new OptionSetValue(3) // Won
};

service.Execute(winRequest);
</code></pre>
<p><strong>Use case:</strong> Automate opportunity closure workflows, such as updating pipeline metrics or notifying stakeholders.</p>
<h2>Conclusion</h2>
<p>Plugins in Microsoft Dynamics 365 CRM give developers the ability to enforce business rules directly within the platform’s execution pipeline. By mastering <strong>context</strong>, <strong>target</strong>, and the use of <strong>IOrganizationService</strong>, you can perform everything from simple CRUD operations to complex queries with FetchXML or QueryExpression.</p>
<p>With these skills, you can:</p>
<ul>
<li><p>Validate and enforce business rules before data is saved.</p>
</li>
<li><p>Automatically create or update related records.</p>
</li>
<li><p>Perform advanced queries for reporting or automation.</p>
</li>
<li><p>Execute system messages to integrate seamlessly with CRM processes.</p>
</li>
</ul>
<p>Learning to harness these capabilities ensures your plugins are both powerful and efficient, helping organizations get the most from their Dynamics 365 CRM investment.</p>
]]></content:encoded></item><item><title><![CDATA[How to Get and Set Lookup Fields in Dynamics 365 CRM (JavaScript, Power Automate, Plugins, Web API)]]></title><description><![CDATA[In this blog, we’ll cover how to work with lookups in four ways:

JavaScript (form scripts)

Power Automate (Flow)

Plugins (C#)

Web API (OData / REST API)


Finally, we’ll walk through real-world ex]]></description><link>https://blog.cleverwizard.com/how-to-get-and-set-lookup-fields-in-dynamics-365-crm-javascript-power-automate-plugins-web-api</link><guid isPermaLink="true">https://blog.cleverwizard.com/how-to-get-and-set-lookup-fields-in-dynamics-365-crm-javascript-power-automate-plugins-web-api</guid><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics 365 crm]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[C#]]></category><category><![CDATA[javascript framework]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Web API]]></category><category><![CDATA[power-automate]]></category><category><![CDATA[PowerPlatform]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Mon, 08 Sep 2025 11:46:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/7572b599-6da7-401d-b83f-806b307232ae.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog, we’ll cover <strong>how to work with lookups in four ways</strong>:</p>
<ul>
<li><p>JavaScript (form scripts)</p>
</li>
<li><p>Power Automate (Flow)</p>
</li>
<li><p>Plugins (C#)</p>
</li>
<li><p>Web API (OData / REST API)</p>
</li>
</ul>
<p>Finally, we’ll walk through <strong>real-world examples</strong> you can apply directly in your projects.</p>
<h2>🔹 1. Handling Lookups with JavaScript in Dynamics 365 CRM</h2>
<p>Form scripts are useful when you want to manipulate lookup fields directly on CRM forms.</p>
<h3>✅ Get a Lookup Value in JavaScript</h3>
<pre><code class="language-javascript">function getLookupValue(executionContext) {
    var formContext = executionContext.getFormContext();
    var lookup = formContext.getAttribute("parentaccountid").getValue();

    if (lookup != null) {
        var id = lookup[0].id;       // GUID of the record
        var name = lookup[0].name;   // Display name
        var entity = lookup[0].entityType; // Entity logical name

        console.log("ID: " + id + " | Name: " + name + " | Entity: " + entity);
    }
}
</code></pre>
<h3>✅ Set a Lookup Value in JavaScript</h3>
<pre><code class="language-javascript">function setLookupValue(executionContext) {
    var formContext = executionContext.getFormContext();

    var lookupValue = [{
        id: "B0D4F3F4-1D3A-4D2A-9A54-8A1B2E6A9F6C",
        name: "Contoso Ltd",
        entityType: "account"
    }];

    formContext.getAttribute("parentaccountid").setValue(lookupValue);
}
</code></pre>
<h2>🔹 2. Handling Lookups in Power Automate (Flow)</h2>
<p>In <strong>Power Automate</strong>, lookup fields need to be set using the <code>@odata.bind</code> notation.</p>
<h3>✅ Get a Lookup Value in Flow</h3>
<p>When retrieving a Contact, a lookup field looks like this:</p>
<pre><code class="language-json">"_parentaccountid_value": "b0d4f3f4-1d3a-4d2a-9a54-8a1b2e6a9f6c"
</code></pre>
<h3>✅ Set a Lookup Value in Flow</h3>
<p>To set a lookup, use:</p>
<pre><code class="language-json">"parentaccountid@odata.bind": "/accounts(b0d4f3f4-1d3a-4d2a-9a54-8a1b2e6a9f6c)"
</code></pre>
<p>💡 In Flow expressions, you can build it dynamically:</p>
<pre><code class="language-java">concat('/accounts(', outputs('Get_Account')?['body/accountid'], ')')
</code></pre>
<h2>🔹 3. Handling Lookups in Plugins (C#)</h2>
<p>In <strong>Dynamics 365 Plugins</strong>, lookups are handled using the <code>EntityReference</code> class.</p>
<h3>✅ Get a Lookup Value in Plugin</h3>
<pre><code class="language-csharp">if (entity.Contains("parentaccountid") &amp;&amp; entity["parentaccountid"] is EntityReference lookup)
{
    Guid id = lookup.Id;
    string name = lookup.Name;       // Sometimes null depending on context
    string logicalName = lookup.LogicalName;
}
</code></pre>
<h3>✅ Set a Lookup Value in Plugin</h3>
<pre><code class="language-csharp">entity["parentaccountid"] = new EntityReference("account", 
    new Guid("B0D4F3F4-1D3A-4D2A-9A54-8A1B2E6A9F6C"));
</code></pre>
<h2>🔹 4. Handling Lookups in Dynamics 365 Web API</h2>
<p>When working with the <strong>Dataverse Web API</strong>, lookup values are accessed using the <code>_fieldname_value</code> convention.</p>
<h3>✅ Get a Lookup Value via Web API</h3>
<pre><code class="language-powershell">GET [Organization URI]/api/data/v9.2/contacts(11111111-2222-3333-4444-555555555555)?$select=fullname,_parentcustomerid_value
</code></pre>
<p>Response:</p>
<pre><code class="language-json">{
  "fullname": "John Doe",
  "_parentcustomerid_value": "b0d4f3f4-1d3a-4d2a-9a54-8a1b2e6a9f6c",
  "_parentcustomerid_value@OData.Community.Display.V1.FormattedValue": "Contoso Ltd",
  "_parentcustomerid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "account"
}
</code></pre>
<ul>
<li><p><code>_parentcustomerid_value</code> → The GUID of the lookup</p>
</li>
<li><p><code>FormattedValue</code> → The display name</p>
</li>
<li><p><code>lookuplogicalname</code> → The entity type</p>
</li>
</ul>
<h3>✅ Set a Lookup Value via Web API</h3>
<pre><code class="language-javascript">PATCH [Organization URI]/api/data/v9.2/contacts(11111111-2222-3333-4444-555555555555)
Content-Type: application/json

{
  "parentcustomerid_account@odata.bind": "/accounts(b0d4f3f4-1d3a-4d2a-9a54-8a1b2e6a9f6c)"
}
</code></pre>
<h2>🔹 Real-World Examples</h2>
<p>Let’s look at some <strong>common business scenarios</strong> where you’ll need to get or set lookup fields.</p>
<h3>Example 1: Setting a Contact’s Parent Account (JavaScript)</h3>
<p>When a user selects an Industry, automatically assign the Contact to a default Account:</p>
<pre><code class="language-javascript">if (formContext.getAttribute("industrycode").getValue() === 42) {
    var accountLookup = [{
        id: "D1C5E8F2-91AA-4C3E-8A14-7C32F1B9F77B",
        name: "Default Industry Account",
        entityType: "account"
    }];
    formContext.getAttribute("parentaccountid").setValue(accountLookup);
}
</code></pre>
<h3>Example 2: Assigning a Record Owner in Power Automate</h3>
<p>When a new Lead is created, assign it to a specific user:</p>
<pre><code class="language-json">"ownerid@odata.bind": "/systemusers(5a4b8f3f-1122-44aa-bbbb-998877665544)"
</code></pre>
<h3>Example 3: Updating Related Account from a Plugin</h3>
<p>If a Contact is marked as "VIP", set its Parent Account to “VIP Clients”:</p>
<pre><code class="language-csharp">if (entity.Contains("new_vipstatus") &amp;&amp; (bool)entity["new_vipstatus"] == true)
{
    entity["parentaccountid"] = new EntityReference("account", 
        new Guid("E2F6B8D1-99F1-4B4D-8CC9-11B2A3D3F1AA"));
}
</code></pre>
<h3>Example 4: Web API — Linking a Case to a Customer</h3>
<pre><code class="language-javascript">PATCH [Organization URI]/api/data/v9.2/incidents(11111111-2222-3333-4444-555555555555)
Content-Type: application/json

{
  "customerid_contact@odata.bind": "/contacts(b0d4f3f4-1d3a-4d2a-9a54-8a1b2e6a9f6c)"
}
</code></pre>
<h2>🎯 Conclusion</h2>
<ul>
<li><p><strong>JavaScript</strong> → Use <code>getValue()</code> and <code>setValue()</code> with <code>{id, name, entityType}</code> objects.</p>
</li>
<li><p><strong>Power Automate (Flow)</strong> → Use <code>@odata.bind</code> with <code>/entityname(guid)</code> syntax.</p>
</li>
<li><p><strong>Plugins (C#)</strong> → Work with <code>EntityReference</code>.</p>
</li>
<li><p><strong>Web API</strong> → Use <code>_lookupfield_value</code> for retrieval and <code>@odata.bind</code> for updates.</p>
</li>
</ul>
<p>Understanding how to get and set lookups across different technologies in Dynamics 365 CRM makes your solutions more <strong>flexible, scalable, and maintainable</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[Tracking Data Changes in Power Automate Using Audit Logs]]></title><description><![CDATA[Why Enable Auditing?
Auditing in Microsoft Dataverse helps in maintaining a log of all data changes. This is important for:

Maintaining a history of data modifications

Meeting compliance and regulat]]></description><link>https://blog.cleverwizard.com/tracking-data-changes-in-power-automate-using-audit-logs</link><guid isPermaLink="true">https://blog.cleverwizard.com/tracking-data-changes-in-power-automate-using-audit-logs</guid><category><![CDATA[Cloud Flow]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics crm]]></category><category><![CDATA[power-automate]]></category><category><![CDATA[audit]]></category><category><![CDATA[powerapps]]></category><category><![CDATA[Power Platform]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Thu, 04 Sep 2025 11:30:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/6ef0bb44-be2c-4014-aad5-de8f0369f439.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Why Enable Auditing?</strong></h2>
<p>Auditing in Microsoft Dataverse helps in maintaining a log of all data changes. This is important for:</p>
<ul>
<li><p>Maintaining a history of data modifications</p>
</li>
<li><p>Meeting compliance and regulatory standards</p>
</li>
<li><p>Troubleshooting data integrity issues</p>
</li>
</ul>
<h2><strong>Enable Auditing in Dataverse</strong></h2>
<p><strong>System Settings:</strong></p>
<ul>
<li><p>Navigate to <em>Power Apps admin centre (</em><a href="https://admin.powerplatform.microsoft.com/home">https://admin.powerplatform.microsoft.com/home</a><em>) &gt; choose your environment &gt; Settings &gt; Audit Settings</em></p>
</li>
<li><p>Under the <em>Auditing</em> tab, ensure start auditing is enabled</p>
</li>
<li><p><img src="https://wonderful-pond-0a78e0200.5.azurestaticapps.net/api/data/v9.0/msdyn_knowledgearticleimages(01df39a9-b93c-f011-877b-000d3af2afe6" alt="Enable Auditing in System Settings" />/msdyn_blobfile/$value align="left")</p>
</li>
</ul>
<p><strong>Enable Table-Level Auditing:</strong></p>
<ul>
<li><p>Select the table (e.g., <code>Account</code>)</p>
</li>
<li><p>Enable “Audit changes to its data” in the table properties (Advanced Options)</p>
  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756984739321/b7f4a09a-6e63-41cd-a2ed-3c8c382581e2.png" alt="" style="display:block;margin:0 auto" /></li>
</ul>
<p><strong>Enable Column-Level Auditing:</strong></p>
<ul>
<li>Select individual columns (e.g., <code>Telephone1</code>)</li>
</ul>
<p>Enable the “Enable Auditing” option</p>
<p><img src="https://wonderful-pond-0a78e0200.5.azurestaticapps.net/api/data/v9.0/msdyn_knowledgearticleimages(da78416c-1f84-f011-b4cc-000d3af2afe6" alt="" />/msdyn_blobfile/$value align="left")</p>
<h2><strong>Create Power Automate Flow to Retrieve Audit Data</strong></h2>
<ol>
<li><p><strong>Trigger:</strong> Use <code>When a row is modified</code> for the table you are tracking.</p>
 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756984960522/8b7c6f12-6919-4def-bd2c-a53353a5ff63.png" alt="" style="display:block;margin:0 auto" />
 </li>
<li><p><strong>List Audit Records:</strong></p>
 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756985020241/9129a394-96ec-4972-a83b-bf22b18543f6.png" alt="" style="display:block;margin:0 auto" />
 
<p> Query Expression:</p>
<pre><code class="language-powershell">objecttypecode eq 'account' and _objectid_value eq 'triggerOutputs()?['body/accountid']'
</code></pre>
<ul>
<li><p>Add a <code>List rows</code> Dataverse action</p>
</li>
<li><p>Use this OData filter query:</p>
</li>
<li><p>Sort by <code>createdon desc</code></p>
</li>
</ul>
</li>
<li><p><strong>Parse Audit Data:</strong></p>
<ol>
<li><p>Use a <code>Parse JSON</code> action:</p>
 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756985085859/99f779ed-b2b5-4f0b-8c3b-2e3639952b41.png" alt="" style="display:block;margin:0 auto" />
 
<p> Content expression:</p>
<pre><code class="language-powershell">first(outputs('List_rows')?['body']?['value'])?['changedata']
</code></pre>
<p> Response:</p>
<pre><code class="language-json">{
  "OldValue": {
    "field1": "Old Value 1"
  },
  "NewValue": {
    "field1": "New Value 2"
  }
}
</code></pre>
</li>
</ol>
</li>
<li><p><strong>Extract Old and New Values:</strong></p>
<ol>
<li><p>Use a <code>Compose</code> action with below expression:</p>
 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756985228206/ee6fe8e7-5d99-4ff3-abe6-673722505511.png" alt="" style="display:block;margin:0 auto" />
 
<pre><code class="language-scss">outputs('Compose')?['changedAttributes'][0]['OldValue']
</code></pre>
<pre><code class="language-scss">outputs('Compose')?['changedAttributes'][0]['NewValue']
</code></pre>
</li>
</ol>
</li>
</ol>
<h2><strong>Conclusion</strong></h2>
<p>Audit logs provide a transparent trail of changes made to data within Dataverse. When combined with Power Automate, they empower users to create intelligent flows that not only respond to changes but also reference historical data. This setup is particularly valuable for quality control, approvals, and governance automation.</p>
<p>We hope this guide helps you understand how to retrieve old and updated data using audit logs in Power Automate. Feel free to leave a comment or reach out for further assistance.</p>
]]></content:encoded></item><item><title><![CDATA[Implementing Dataverse Search Using Web API in PowerApps and Dynamics 365 CE]]></title><description><![CDATA[In this blog post, we'll explore how to access Dataverse using the Web API to implement search functionality. This approach allows you to retrieve and manage data stored in Dataverse (formerly Common ]]></description><link>https://blog.cleverwizard.com/implementing-dataverse-search-using-web-api-in-powerapps-and-dynamics-365-ce</link><guid isPermaLink="true">https://blog.cleverwizard.com/implementing-dataverse-search-using-web-api-in-powerapps-and-dynamics-365-ce</guid><category><![CDATA[Dynamics CE]]></category><category><![CDATA[C#]]></category><category><![CDATA[Dynamics CRM Partner ]]></category><category><![CDATA[PowerPlatform]]></category><category><![CDATA[Dataverse]]></category><dc:creator><![CDATA[Aman Upadhyay (CleverWizard)]]></dc:creator><pubDate>Wed, 03 Sep 2025 14:40:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68b81cd5eff2027936ba607e/cb77a3d3-9a42-43ad-9839-7080c697d3c4.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog post, we'll explore how to access Dataverse using the Web API to implement search functionality. This approach allows you to retrieve and manage data stored in Dataverse (formerly Common Data Service) efficiently. Whether you're building a web application or integrating with other services, understanding how to leverage the Web API for search is essential.</p>
<h2><strong>What is Dataverse Search?</strong></h2>
<p>Dataverse (formerly known as Common Data Service) is a cloud-based data storage and management platform provided by Microsoft. It allows users to store and manage data used by business applications. The Dataverse Search functionality enables users to perform searches across the data stored within Dataverse, providing a powerful way to query and retrieve relevant information.</p>
<p>By using Dataverse Search, you can query various types of data, including entities and fields, and filter results based on specific criteria. This feature is particularly useful for applications that need to provide search capabilities to end users, allowing them to find data quickly and efficiently.</p>
<h2><strong>Why Use Web API for Dataverse Search?</strong></h2>
<p>Accessing Dataverse using the Web API provides several advantages:</p>
<ul>
<li><p><strong>Flexibility:</strong> The Web API allows for dynamic querying and integration with various applications, offering greater control over how data is accessed and manipulated.</p>
</li>
<li><p><strong>Scalability:</strong> Web APIs are designed to handle a large number of requests and can scale according to the needs of your application.</p>
</li>
<li><p><strong>Standard Protocol:</strong> The Web API uses standard HTTP methods (GET, POST, PUT, DELETE), making it easy to integrate with different technologies and platforms.</p>
</li>
<li><p><strong>Security:</strong> The Web API supports OAuth 2.0 for secure authentication and authorization, ensuring that only authorized users can access the data.</p>
</li>
</ul>
<p>Using the Web API for Dataverse search allows you to harness these benefits and build robust, scalable applications that can interact with Dataverse data seamlessly.</p>
<h2><strong>Implementation</strong></h2>
<p>Below are two code examples demonstrating how to implement search in Dataverse using the Web API. The first example shows how to perform a search, and the second example demonstrates how to handle the API call to retrieve results.</p>
<h3><strong>Performing the Search</strong></h3>
<pre><code class="language-csharp">public async Task&lt;HttpResponseData&gt; SearchKWArticles()
{
    try
    {
        // Retrieve an access token for authentication from a helper method
        String token = await GetAccessTokenAsync();
        
        // Initialize the response variable
        HttpResponseData response = null;
        
        // Define hardcoded search parameters
        string searchQuery = "sample search text";  // The search query string
        bool countResults = true;  // Whether to count the number of results
        int topResults = 10;  // Maximum number of results to return
        string[] orderBy = new string[] { "createdon desc" };  // Sort results by creation date in descending order
        string filter = "createdon gt 1900-01-01";  // Filter results based on creation date
        
        // Define the Web API endpoint for performing the search
        String urlForConfigs = "/api/search/v2.0/query";
        
        // Construct the JSON request body for the Web API
        var jsonRequest = JsonConvert.SerializeObject(new
        {
            search = searchQuery,  // The search term
            count = countResults,  // Whether to count the results
            top = topResults,  // Limit on the number of results
            entities = JsonConvert.SerializeObject(new List&lt;object&gt;
            {
                new {
                    name = "knowledgearticle",  // The entity to search within
                    selectColumns = new List&lt;string&gt; { "title", "createdon","articlepublicnumber","description" },  // Columns to select
                    searchColumns = new List&lt;string&gt; { "title","articlepublicnumber","description" },  // Columns to search
                    filter = "statecode eq 3 and isprimary eq true"  // Filter to include only primary articles
                },
            }),
            orderby = JsonConvert.SerializeObject(orderBy),  // Sorting order
            filter = filter  // Additional filtering criteria
        });
        
        // Call the method to retrieve search results from the Web API
        String data = await RetrieveSuggestions(token, urlForConfigs, jsonRequest);
        
        // Create and configure the HTTP response with the search results
        response = req.CreateResponse(HttpStatusCode.OK);
        response.Headers.Add("Content-Type", "application/json; charset=utf-8");
        response.WriteString(data.Replace(_baseUrl, _impersonateUrl));  // Replace base URL if needed
        
        return response;
    }
    catch (Exception ex)
    {
        // Handle exceptions by returning a bad request response with the error message
        return CreateBadRequestResponse(req, ex.Message);
    }
}
</code></pre>
<h3><strong>Retrieving Search Results</strong></h3>
<pre><code class="language-csharp">private async Task&lt;string&gt; RetrieveSuggestions(string accessToken, string queryURL, object postBody)
{
    try
    {
        // Set the authorization header with the Bearer token for authentication
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        
        // Construct the full URL for the API request
        String url = _baseUrl + queryURL;
        
        // Send a POST request with the JSON body to the Web API
        var response = await _httpClient.PostAsJsonAsync(url, postBody);
        
        // Ensure the response indicates success
        response.EnsureSuccessStatusCode();
        
        // Read and return the response content as a string
        string content = await response.Content.ReadAsStringAsync();
        return content;
    }
    catch (Exception ex)
    {
        // Return error message in case of an exception
        return ex.Message;
    }
}
</code></pre>
<h3><strong>Additional Explanations</strong></h3>
<p>Here are some additional explanations to help you understand the code:</p>
<ul>
<li><p><strong>Access Token:</strong> The <code>GetAccessTokenAsync</code> method retrieves an OAuth 2.0 token needed for authenticating requests to the Web API. This token ensures secure access to the Dataverse data.</p>
</li>
<li><p><strong>Search Parameters:</strong></p>
<ul>
<li><p><code>searchQuery</code>: Defines the text or keywords used in the search.</p>
</li>
<li><p><code>countResults</code>: Determines whether to include the count of search results.</p>
</li>
<li><p><code>topResults</code>: Limits the number of results returned.</p>
</li>
<li><p><code>orderBy</code>: Specifies how the results should be sorted.</p>
</li>
<li><p><code>filter</code>: Applies additional conditions to filter the results based on specific criteria.</p>
</li>
</ul>
</li>
<li><p><strong>RetrieveSuggestions Method:</strong></p>
<ul>
<li><p>Sets the HTTP request header with the access token for authentication.</p>
</li>
<li><p>Sends the JSON payload to the Web API endpoint using a POST request.</p>
</li>
<li><p>Reads and returns the response from the API call, handling any exceptions by returning error messages.</p>
</li>
</ul>
</li>
</ul>
<h2><strong>Conclusion</strong></h2>
<p>Accessing Dataverse using the Web API enables you to perform powerful searches and retrieve relevant data efficiently. The provided code and explanations should help you integrate search capabilities into your applications and leverage the full potential of Dataverse.</p>
]]></content:encoded></item></channel></rss>