Diversi clienti si sono avvicinati a noi e ci hanno chiesto come possono creare un’app Web simile a Google Docs utilizzando le nostre API. Google Docs è un elaboratore di testi che consente agli utenti di creare e modificare file online mentre collaborano con altri utenti in tempo reale.

Questo post del blog spiega quanto sia facile creare una versione ridotta di Google Documenti che abbia le seguenti caratteristiche:

  • Rich Text Editing (modifica carattere del testo, dimensione, colore, stile (grassetto, corsivo), allineamento ecc.).
  • Modifica collaborativa in tempo reale dello stesso documento. Più utenti possono accedere al documento contemporaneamente e modificarlo.
  • Carica il contenuto di un documento Word esistente in un editor.
  • Salva il testo nell’editor come documento MS Word, PDF, TXT o HTML.

Il nostro prodotto finale avrà il seguente aspetto:

Google Docs come l'interfaccia dell'app

Strumenti e tecnologie: crea documenti Google come un’app

Svilupperemo l’app Web come Google Docs in ASP.NET Core e utilizzeremo le due librerie seguenti:

  • Focolare is an open-source, collaborative text editor. It uses the Firebase Realtime Database as a backend so it requires no server-side code and can be added to any web app simply by including the JavaScript files.
  • GroupDocs.Editor per .NET gives us an ability to edit most popular document formats using any WYSIWYG editor without any additional applications. We will load document via GroupDocs.Editor into Firepad, edit document in a way we want and save it back to original document format.

Ho usato Visual Studio per Mac come IDE. Tuttavia, puoi scaricare l’edizione gratuita della community di Visual Studio, a seconda della piattaforma, da qui. Iniziamo.

Creare un nuovo progetto di applicazione Web ASP.NET Core e denominare il progetto “GoogleDocsLite”.

Creare una nuova app Web ASP.NET Core

Esegui l’applicazione per assicurarti che tutto sia impostato correttamente.

Integra Firepad

Possiamo aggiungere Firepad alla nostra app Web includendo i seguenti file JavaScript nel file sezione di Layout.cshtml.

<!-- Firebase -->
<script src="https://www.gstatic.com/firebasejs/7.13.2/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.13.2/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.13.2/firebase-database.js"></script>

<!-- CodeMirror -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.css" />

<!-- Firepad -->
<link rel="stylesheet" href="https://firepad.io/releases/v1.5.9/firepad.css" />
<script src="https://firepad.io/releases/v1.5.9/firepad.min.js"></script>

<!-- userlist -->
<script src="~/js/firepad-userlist.js"></script>
<link rel="stylesheet" href="~/css/firepad-userlist.css" />

Per creare un Firepad, inizializzeremo Firebase, CodeMirror e quindi Firepad. Aggiungi lo script e il codice HTML seguenti in Index.cshtml.

<script>
    function init() {
        // Initialize Firebase.
        // TODO: replace with your Firebase project configuration.
        var config = {
            apiKey: '',
            authDomain: "",
            databaseURL: ""
        };
        firebase.initializeApp(config);
        
        // Get Firebase Database reference.
        var firepadRef = getExampleRef();
        
        // Create CodeMirror (with lineWrapping on).
        var codeMirror = CodeMirror(document.getElementById('firepad'), { lineWrapping: true });

        // Create a random ID to use as our user ID (we must give this to firepad and FirepadUserList).
        var userId = Math.floor(Math.random() * 9999999999).toString();

        // Create Firepad (with rich text features and our desired userId).
        var firepad = Firepad.fromCodeMirror(firepadRef, codeMirror,
                    { richTextToolbar: true, richTextShortcuts: true, userId: userId });

        // Create FirepadUserList (with our desired userId).
        var firepadUserList = FirepadUserList.fromDiv(firepadRef.child('users'),
        document.getElementById('userlist'), userId);
    }
    
    // Helper to get hash from end of URL or generate a random one.
    function getExampleRef() {
        var ref = firebase.database().ref();
        var hash = window.location.hash.replace(/#/g, '');
        if (hash) {
            ref = ref.child(hash);
        } else {
            ref = ref.push(); // generate unique location.
            window.location = window.location + '#' + ref.key; // add it as a hash to the URL.
        }
        if (typeof console !== 'undefined') {
            console.log('Firebase data: ', ref.toString());
        }
        return ref;
    }
</script>

<div id="userlist"></div>
<div id="firepad"></div>

Sostituisci il contenuto di config con config del tuo progetto Firebase.

Vogliamo che lo script di cui sopra venga eseguito una volta che una pagina Web ha caricato completamente tutto il contenuto (file di script, file CSS, ecc.). Quindi, chiama la funzione init() dall’attributo dell’evento onLoad di elemento in Layout.cshtml.

<body onload="init()">

Il tuo <body> dovrebbe apparire come segue. Se contiene tag non necessari come <header> , <footer> , per favore rimuovili.

Elemento del corpo

Se esegui il progetto, noterai che firepad e userlist non sono allineati correttamente. Si prega di utilizzare il seguente codice CSS per regolare la dimensione/posizione del firepad e dell’elenco utenti. È possibile aggiungere il seguente codice all’interno elemento di Layout.cshtml.

<style>
    html {
        height: 100%;
    }

    body {
        margin: 0;
        height: 100%;
    }

    /* We make the user list 175px and firepad fill the rest of the page. */
    #userlist {
        position: absolute;
        left: 0;
        top: 50px;
        bottom: 0;
        height: auto;
        width: 175px;
    }

    #firepad {
        position: absolute;
        left: 175px;
        top: 50px;
        bottom: 0;
        right: 0;
        height: auto;
    }
</style>

Firepad è stato configurato correttamente.

Carica il contenuto di un documento Word esistente in un editor

Ora vogliamo offrire ai nostri utenti un modo per caricare il contenuto di un documento Word esistente nell’editor di testo. Sul frontend, aggiungiamo un elemento di tipo file che consente a un utente di selezionare un documento di Word dal proprio computer locale. Sul back-end, utilizziamo la libreria GroupDocs.Editor per recuperare il contenuto di un documento Word come stringa HTML. Infine, utilizziamo il metodo setHtml() di Firepad per mostrare il contenuto nell’editor di testo.

Aggiungi quanto segue elemento nel file Index.cshtml prima etichetta.

<form method="post" enctype="multipart/form-data" id="uploadForm">
    <input asp-for="UploadedDocument" />
    <input type="submit" value="Upload Document" class="btn btn-primary" asp-page-handler="UploadDocument" />
</form>

Nel file Index.cshtml.cs, definire una proprietà corrispondente.

[BindProperty]
public IFormFile UploadedDocument { get;  set; }

Esegui il progetto e fai clic sul pulsante Scegli file. Seleziona un documento Word che desideri caricare e fai clic sul pulsante Carica documento. Non accadrà nulla perché non abbiamo ancora definito il gestore in Index.cshtml.cs. Prima di farlo, aggiungiamo prima la libreria GroupDocs.Editor nel nostro progetto.

Integra GroupDocs.Editor

GroupDocs.Editor è disponibile come pacchetto NuGet in modo da poterlo aggiungere facilmente al nostro progetto. Fare clic con il pulsante destro del mouse sul progetto e selezionare l’opzione Gestisci pacchetti NuGet. Verrà visualizzata la finestra Gestisci pacchetti NuGet, selezionare la scheda Sfoglia e immettere GroupDocs.Editor nel campo di ricerca. Come primo risultato dovrebbe apparire GroupDocs.Editor, selezionarlo e quindi fare clic sul pulsante Aggiungi pacchetto.

Aggiungi GroupDocs.Editor tramite NuGet Package Manager

Quando il pacchetto viene aggiunto correttamente, verrà visualizzato nella sottocartella NuGet nella cartella Dipendenze.

Gestione dei dati dei moduli

Ora scriviamo un gestore (metodo OnPostUploadDocument()) che verrà chiamato quando un utente fa clic sul pulsante Carica documento. L’oggetto UploadedDocument (di tipo IFormFile) contiene il contenuto del documento caricato. Innanzitutto, salviamo il documento sul server, quindi utilizziamo la libreria GroupDocs.Editor per ottenere il suo contenuto come stringa HTML. Aggiungi il codice seguente nel file Index.cshtml.cs.

private readonly IWebHostEnvironment _hostingEnvironment;

public string DocumentContent { get; set; }

public IndexModel(IWebHostEnvironment hostingEnvironment)
{
    _hostingEnvironment = hostingEnvironment;
}

public void OnPostUploadDocument()
{
    var projectRootPath = Path.Combine(_hostingEnvironment.ContentRootPath, "UploadedDocuments");
    var filePath = Path.Combine(projectRootPath, UploadedDocument.FileName);
    UploadedDocument.CopyTo(new FileStream(filePath, FileMode.Create));
    ShowDocumentContentInTextEditor(filePath);
}

private void ShowDocumentContentInTextEditor(string filePath)
{
    WordProcessingLoadOptions loadOptions = new WordProcessingLoadOptions();
    Editor editor = new Editor(filePath, delegate { return loadOptions; }); //passing path and load options (via delegate) to the constructor
    EditableDocument document = editor.Edit(new WordProcessingEditOptions()); //opening document for editing with format-specific edit options

    DocumentContent = document.GetContent();
}

Firepad fornisce due eventi per l’ascolto. Uno di questi è “pronto” che si attiva una volta che Firepad ha recuperato il contenuto iniziale dell’editor. Alleghiamo un callback a questo tipo di evento e nel callback, passiamo la stringa DocumentContent come argomento al metodo setHtml() dell’oggetto firepad. Aggiungi il codice seguente nella funzione init() in Index.cshtml.

firepad.on('ready', function () {
    if (firepad.isHistoryEmpty()) {
        var documentContent = '@Model.DocumentContent';
        if (documentContent.length != 0) {   
            firepad.setHtml(htmlDecode(documentContent));
        } else {
            firepad.setText("Welcome to your own private pad! Share the URL above and collaborate with your friends.");
        }
    }
});

Potresti aver notato che abbiamo passato la stringa documentContent prima al metodo htmlDecode() prima di passare al metodo setHtml(). Serve per sostituire le entità carattere come <, > con segni (< e >). Il metodo htmlDecode() ha il seguente aspetto.

function htmlDecode(input) {
    var e = document.createElement('div');
    e.innerHTML = input;
    return e.childNodes[0].nodeValue;
}

Esegui il progetto, ora dovresti essere in grado di caricare il contenuto di un documento di Word in un editor.

Nella parte II di questo post, ho spiegato come possiamo estendere la nostra applicazione per includere le seguenti funzionalità:

  • Scarica il contenuto dell’editor come documento MS Word, PDF, TXT o HTML.
  • Condividi l’URL con gli amici in modo che possano modificare il documento contemporaneamente.

Si prega di controllarlo.

Il codice sorgente completo del progetto è disponibile su GitHub.

Guarda anche