<%@ Page Title="Remote sources (cloud picker)" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" %>
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitleContent" runat="server">Remote sources - AjaxUploader v5.2 Demos</asp:Content>
<asp:Content ID="HeaderContent" ContentPlaceHolderID="HeaderTitle" runat="server">Remote sources (Google Drive, Dropbox, OneDrive, - </asp:Content>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server"> 
<div class="demo-card"> <div class="demo-card-header">Pull files straight from cloud storage - no local copy needed</div> <div class="demo-card-body"> <p class="demo-description"> <strong>Six built-in cloud sources.</strong> AjaxUploader ships first-class pickers for <strong>Google Drive, OneDrive, Dropbox, Box, Unsplash, and the Companion broker</strong> - browser OAuth implicit flow for the direct pickers, or server-side OAuth via the Companion broker when the browser should never see a token. Each picked file's download URL flows into the server-side <code>importFromUrl()</code> pipeline, streamed through your provider with the same NDJSON live-progress contract as URL import. Extend further via <code>AjaxUploader.registerRemoteSource(name, source)</code> for any custom file portal (SharePoint, Egnyte, internal DMS).
 </p> <div class="demo-expect"><strong>What you should see:</strong> import from Google Drive beside local browse - the file streams through your import endpoint, not the browser's token.</div> <div class="alert alert-warning mt-3" role="alert"> <strong>Setup required.</strong> The Google Drive button below renders disabled until you configure an API key + OAuth client ID:
 <ol class="mt-2 mb-0"> <li>Create a project at <code>console.cloud.google.com</code>, enable the <em>Picker API</em>.</li> <li>Create an API key (Credentials -> API key).</li> <li>Create an OAuth 2.0 Client ID (Credentials -> Create -> OAuth client ID -> Web application). Add your origin to "Authorized JavaScript origins".</li> <li>Configure the adapter before <code>create()</code>:
<pre style="margin:0.5rem 0 0;background:rgba(0,0,0,0.04);padding:0.75rem;border-radius:0.5rem;font-size:0.8rem;"><code>AjaxUploader.configureRemoteSource('googledrive', {
 apiKey: 'YOUR_API_KEY',
 clientId: 'YOUR_OAUTH_CLIENT_ID'
});</code></pre> </li> </ol> </div> <div class="d-flex gap-2 mb-2 mt-4">
 <button type="button" class="btn btn-primary" id="startBtn_remoteUploader">Start upload</button>
 <button type="button" class="btn btn-outline-secondary" id="clearBtn_remoteUploader">Clear queue</button>
 </div>
 <div id="remoteUploader"></div> <script> (function () {
 function init() {
 if (!window.AjaxUploader || typeof AjaxUploader.create !== 'function') return;
 var uploader = AjaxUploader.create('#remoteUploader', {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 multiple: true,
 autoUpload: false,
 remoteSources: ['googledrive']
 });
 var startButton = document.getElementById('startBtn_remoteUploader');
 var clearButton = document.getElementById('clearBtn_remoteUploader');
 if (startButton) {
 startButton.addEventListener('click', function () {
 if (uploader && typeof uploader.upload === 'function') uploader.upload();
 });
 }
 if (clearButton) {
 clearButton.addEventListener('click', function () {
 if (uploader && typeof uploader.clear === 'function') uploader.clear();
 });
 }
 }
 if (document.readyState !== 'loading') init();
 else document.addEventListener('DOMContentLoaded', init);
 })();
 </script> <h5 class="mt-4">Registering a custom source</h5> <div class="code-panel">
<pre><code>AjaxUploader.registerRemoteSource('dropbox', {
 title: 'Dropbox',
 icon: '&lt;svg viewBox=...&gt;...&lt;/svg&gt;', <span style="color:#94a3b8">// SVG markup or img URL</span> ready: function () { <span style="color:#94a3b8">return</span> !!window.Dropbox; },

 pick: function (uploader) {
 <span style="color:#94a3b8">return new</span> Promise(<span style="color:#94a3b8">function</span> (resolve, reject) {
 window.Dropbox.choose({
 multiselect: <span style="color:#94a3b8">true</span>,
 linkType: <span style="color:#94a3b8">'direct'</span>,
 success: <span style="color:#94a3b8">function</span> (files) {
 resolve(files.map(<span style="color:#94a3b8">function</span> (f) {
 <span style="color:#94a3b8">return</span> { url: f.link, fileName: f.name, fileSize: f.bytes };
 }));
 },
 cancel: <span style="color:#94a3b8">function</span> () { reject(<span style="color:#94a3b8">new</span> Error(<span style="color:#94a3b8">'cancelled'</span>)); }
 });
 });
 }
});

AjaxUploader.create(<span style="color:#94a3b8">'#uploader'</span>, {
 remoteSources: [<span style="color:#94a3b8">'googledrive'</span>, <span style="color:#94a3b8">'dropbox'</span>]
});</code></pre> </div> <h5 class="mt-4"><code>RemoteFile</code> shape returned from <code>pick()</code></h5> <div class="code-panel">
<pre><code>{
 url: <span style="color:#94a3b8">'https://www.googleapis.com/drive/v3/files/abc?alt=media'</span>,
 fileName: <span style="color:#94a3b8">'quarterly-report.pdf'</span>,
 fileSize: <span style="color:#94a3b8">2_457_600</span>, <span style="color:#94a3b8">// optional</span> contentType: <span style="color:#94a3b8">'application/pdf'</span>, <span style="color:#94a3b8">// optional</span> headers: { <span style="color:#94a3b8">'Authorization'</span>: <span style="color:#94a3b8">'Bearer ya29...'</span> } <span style="color:#94a3b8">// optional</span>
}</code></pre> </div> <h5 class="mt-4">Wire flow</h5> <ol class="mb-3"> <li>User clicks the source button rendered in the uploader chrome.</li> <li>Adapter's <code>pick()</code> runs - typically opens an OAuth popup + provider's native file picker.</li> <li>On select -> resolves with a <code>RemoteFile</code> (or array).</li> <li>Each item is handed to <code>uploader.importFromUrl(url, { fileName, contentType, headers })</code>.</li> <li>The server <code>/import-url</code> endpoint fetches the URL (using any provided <code>Authorization</code> header) and streams through your <code>IUploaderProvider</code> with live NDJSON progress.</li> </ol> <h5 class="mt-4">Built-in: Google Drive (no server broker needed)</h5> <p>Uses Google Picker + Identity Services. The browser opens the OAuth popup, gets an access token, passes it to Picker, then forwards the token as <code>Authorization</code> when the server fetches the file from Drive's REST API. The token never goes to your server - just the URL + the bearer.</p> <h5 class="mt-4">Also available</h5> <ul class="mb-3"> <li><strong><a href="CompanionBroker.aspx">Companion broker</a></strong> - a self-hosted Node OAuth broker for Dropbox, Box, Drive, and OneDrive. Tokens stay server-side and never reach the browser; supports proxy and server-to-destination modes.</li> <li><strong><a href="UnsplashSource.aspx">Unsplash source</a></strong> - search and import royalty-free imagery directly into the queue.</li> <li><strong>Per-source MIME / size filters</strong> at picker-time via <code>options</code>.</li> </ul> </div>
</div> 
</asp:Content>
