# Save rich text docs in github (github integration)

**URL:** <https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212>\
**Category:** Ideas & Features\
**Created:** [March 31, 2023, 2:13pm UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212 "2023-03-31T14:13:51Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jose\_Quesada](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/jose_quesada/32/5458_2.png) [@Jose\_Quesada](https://community.fibery.io/u/Jose_Quesada)\
**Post date:** [March 31, 2023, 2:13pm UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212/1 "2023-03-31T14:13:51Z")

</div>

Right now, I’m terrified of building too much inside fibery because there’s no way out. There’s a csv export, or copy pasting each doc individually. Both inadequate solutions for peace of mind.

I see there’s some [github integration](https://the.fibery.io/search/Narve#User_Guide/Guide/GitHub-integration-71) but I don’t think this applies to content in rich text docs.

If we could associate all rich text docs in a space to a github repo, using the hooks they allow for integration… BOOM, we would have a backup of the content inside fibery (at least text; I understand there’s a lot more that would need far more sophisticated solutions).

Is this even remotely possible? Thanks!

---

<div class="post-metadata">

**Author:** ![itsu](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/itsu/32/1775_2.png) [@itsu](https://community.fibery.io/u/itsu)\
**Post date:** [March 31, 2023, 2:23pm UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212/2 "2023-03-31T14:23:16Z")

</div>

Hello, Jose.

[The workspace export](https://the.fibery.io/@public/User_Guide/Guide/Export-your-data-63) feature was implemented precisely with the “peace of mind” goal. It gives you a hard copy of your data in a workspace. No need to copy paste anything.

GitHub integration syncs stuff from GitHub to Fibery, so different direction.

---

<div class="post-metadata">

**Author:** ![Jose\_Quesada](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/jose_quesada/32/5458_2.png) [@Jose\_Quesada](https://community.fibery.io/u/Jose_Quesada)\
**Post date:** [April 6, 2023, 12:14pm UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212/3 "2023-04-06T12:14:00Z")

</div>

> 🚨 Only CSV. No Markdown (no `Rich text` field & no `Comments` field)

That’s the problem. I want to use fibery as mostly an internal wiki. No markdown is a show stopper

---

<div class="post-metadata">

**Author:** ![mdubakov](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/mdubakov/32/10_2.png) [@mdubakov](https://community.fibery.io/u/mdubakov)\
**Post date:** [April 6, 2023, 12:22pm UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212/4 "2023-04-06T12:22:28Z")

</div>

Hmm, you can export the whole workspace with all markdown files and all databases as csv tables.

---

<div class="post-metadata">

**Author:** ![Chr1sG](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/chr1sg/32/3941_2.png) [@Chr1sG](https://community.fibery.io/u/Chr1sG)\
**Post date:** [April 6, 2023, 12:49pm UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212/5 "2023-04-06T12:49:59Z")

</div>

> [@Jose\_Quesada](#):
>
> Only CSV. No Markdown (no `Rich text` field & no `Comments` field)

This only applies to exports from table view.  
As @mdubakov says

> [@mdubakov](#):
>
> you can export the whole workspace with all markdown files and all databases as csv tables

---

<div class="post-metadata">

**Author:** ![seaotternerd](https://avatars.discourse-cdn.com/v4/letter/s/b5a626/32.png) [@seaotternerd](https://community.fibery.io/u/seaotternerd)\
**Post date:** [April 7, 2023, 3:12am UTC](https://community.fibery.io/t/save-rich-text-docs-in-github-github-integration/4212/6 "2023-04-07T03:12:03Z")

</div>

I agree with what the team said about exporting. That said, if you’re looking specifically to push you rich text documents to github, here’s a script that does so which can be used as part of an automation (I have a button that uses this to push the current version of a file to github). It assumes that your entity has a text field called “Github SHA”. One kind of annoying thing is that it names the file according to the entity id, but it’s workable:

```auto
// Developer reference is at api.fibery.io/#action-buttons

var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

function InvalidCharacterError(message) {
    this.message = message;
}
InvalidCharacterError.prototype = new Error();
InvalidCharacterError.prototype.name = 'InvalidCharacterError';

// encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
function btoa(input) {
    var str = String(input);
    for (
        // initialize result and counter
        var block, charCode, idx = 0, map = chars, output = '';
        // if the next str index does not exist:
        // change the mapping table to "="
        // check if d has no fractional digits
        str.charAt(idx | 0) || (map = '=', idx % 1);
        // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
        output += map.charAt(63 & block >> 8 - idx % 1 * 8)
    ) {
        charCode = str.charCodeAt(idx += 3 / 4);
        if (charCode > 0xFF) {
            throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
        }
        block = block << 8 | charCode;
    }
    return output;
}

// Fibery API is used to retrieve and update entities
const fibery = context.getService('fibery');

// HTTP allows to send requests to external services
const http = context.getService('http');

// affected entities are stored in args.currentEntities;
// to support batch actions they always come in an array
for (const entity of args.currentEntities) {

    var desc = await fibery.getDocumentContent(entity.Description.Secret);

    var url = "https://api.github.com/repos/[your username here]/[your repo name here]/contents/" + entity["Id"] + ".md"

    var response = await http.putAsync(url, {
        body: {
            message: "Update " + entity.Name,
            content: btoa(desc),
            sha: entity["Github SHA"]
        },
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': 'token [github token here]',
            'User-Agent': '[github username here]'
        }
    });
    console.log(response);

    // to update an entity provide an object with the new values
    await fibery.updateEntity(entity.type, entity.id, {
    'Github SHA': response["content"]["sha"]
    });
}

```

You can also pull the current version of the file from github using this script:

```auto
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

function InvalidCharacterError(message) {
    this.message = message;
}
InvalidCharacterError.prototype = new Error();
InvalidCharacterError.prototype.name = 'InvalidCharacterError';

// encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
function btoa(input) {
    var str = String(input);
    for (
        // initialize result and counter
        var block, charCode, idx = 0, map = chars, output = '';
        // if the next str index does not exist:
        // change the mapping table to "="
        // check if d has no fractional digits
        str.charAt(idx | 0) || (map = '=', idx % 1);
        // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
        output += map.charAt(63 & block >> 8 - idx % 1 * 8)
    ) {
        charCode = str.charCodeAt(idx += 3 / 4);
        if (charCode > 0xFF) {
            throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
        }
        block = block << 8 | charCode;
    }
    return output;
}

// decoder
// [https://gist.github.com/1020396] by [https://github.com/atk]
function atob(input) {
    var str = (String(input)).replace(/[=]+$/, ''); // #31: ExtendScript bad parse of /=
    if (str.length % 4 === 1) {
        throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.");
    }
    for (
        // initialize result and counters
        var bc = 0, bs, buffer, idx = 0, output = '';
        // get next character
        buffer = str.charAt(idx++); // eslint-disable-line no-cond-assign
        // character found in table? initialize bit storage and add its ascii value;
        ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
            // and if not first of each 4 characters,
            // convert the first 8 bits to one ascii character
            bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
    ) {
        // try to find character in table (0-63, not found => -1)
        buffer = chars.indexOf(buffer);
    }
    return output;
}

function atob_newlines(input) {
    var lines = String(input).split("\n");
    var result = "";
    console.log(lines);
    for (var i = 0; i < lines.length; i++) {
        console.log(lines[i]);
        result += atob(lines[i]);
    }
    return result;
}

// Fibery API is used to retrieve and update entities
const fibery = context.getService('fibery');

// HTTP allows to send requests to external services
const http = context.getService('http');

// affected entities are stored in args.currentEntities;
// to support batch actions they always come in an array
for (const entity of args.currentEntities) {

    var url = "https://api.github.com/repos/[owner of your repo]/[your repo name]/contents/" + entity["Id"] + ".md";
    var response = await http.getAsync(url, {
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': 'token [your github token]',
            'User-Agent': '[your github username]'
        }
    });
    response = JSON.parse(response);

    // to update an entity provide an object with the new values
    await fibery.setDocumentContent(entity.Description.Secret, atob_newlines(response["content"]));
    await fibery.updateEntity(entity.type, entity.id, {
        "Github SHA": response["sha"]
    });
}

```
