UNPKG

bloggify-cli

Version:
943 lines (670 loc) 20.5 kB
# Bloggify Utils The [`bloggify-utils`](https://github.com/Bloggify/bloggify-utils) module is a utility module that provides some useful functions and objects for Bloggify applications. It provides util `actions`, authentication handlers, CSS assets, common error codes, geolocation utilities, common models, React components and common `services`. ## Installation ```sh npm i -S bloggify-utils ``` ## Usage ```js // In the app/actions/db.js module.exports = require("bloggify-utils/lib/actions/db"); ``` ## Configuration The following configuration is mandatory: - `imports.dir`: The directory where the imported files will be saved (such as csv files). - `uploads.dir`: The directory where the uploaded files will be saved. ## Errors - MISSING_DATA (`<1>`) - NOT_AUTHORIZED - NOT_FOUND - VALIDATION_FAILED (`<1>`) ## Models ### ActivityLogItem - `action`: `STRING` - `model`: `STRING` - `item_id`: `INTEGER` - `metadata`: `JSON` - `actor_email`: `STRING` ### File - `originalname`: `STRING - `encoding`: `STRING - `mimetype`: `STRING - `filename`: `STRING - `extension`: `STRING - `size`: `INTEGER` ### Settings - `settings`: `JSON` ## Services ### `ActivityLog` #### `ActivityLog.log(data)` Logs an activity. ```js Bloggify.services.ActivityLog.log.call(ctx, { action: "create", model: "User", item_id: 42, metadata: { name: "John" } }) ``` #### `ActivityLog.getLogs()` Gets a list of activity logs. ```js const logs = await Bloggify.services.ActivityLog.getLogs({ where: { action: "create" } }); ``` ### `Auth` #### `Auth.beforeActionsRequestRole(role)` Verifies if the user has the required role to access the action. ```js exports.before = Bloggify.services.Auth.beforeActionsRequestRole("admin") ``` ### `Database` #### `Database.importCsv(data)` Imports a CSV file. ##### Params - `data` (Object): An object containing: - `model` (String): The model name. - `path` (String): The path to the CSV file. ##### Example ```js Bloggify.services.Database.importCsv({ model: "User", path: "/path/to/file.csv" }) ``` #### `Database.exportCsv(data)` Export the records of a model to a CSV file. ##### Params - `data` (Object): An object containing: - `model` (String): The model name. - `path` (String): The path to the CSV file. ##### Example ```js const exportedFilePath = await Bloggify.services.Database.exportCsv({ model: "User" }) ``` #### `Database.query(data)` Query the database. ##### Params - `data` (Object): An object containing: - `page` (Number): The page number. - `perPage` (Number): The number of items per page. - `search` (String): The search query. - `model` (String): The model name. - `sorting` (Array): An array of objects containing: - `where` (Object): The where condition. - `include` (Array): The included models. ##### Returns An object containing: - `count` (Number): The total number of records. - `rows` (Array): The records. - `pageCount` (Number): The total number of pages. - `model` (String): The model name. ##### Example ```js const data = await Bloggify.services.Database.query({ model: "User", page: 1, perPage: 10, search: "John", where: { age: { $gt: 42 } }, sorting: [ { field: "name", direction: "ASC" } ], }); ``` #### `Database.beforeCreateOrUpdate(data)` A hook that is called before creating or updating a record. ##### Example ```js Bloggify.services.Database.beforeCreateOrUpdate = async ({ instance, operationType, model, ctx }) => { // ... }); ``` #### `Database.delete(data)` Delete a record from the database. ##### Params - `data` (Object): An object containing: - `model` (String): The model name. - `id` (Number): The record ID. ##### Example ```js const deletedRecord = await Bloggify.services.Database.delete({ model: "User", id: 42 }); ``` #### `Database.getExistingColumnValues(data)` Get the existing column values. ##### Params - `data` (Object): An object containing: - `model` (String): The model name. - `column` (String): The column name. ##### Returns An object containing: - `values` (Array): The existing column values. - `model` (String): The model name. - `column` (String): The column name. ##### Example ```js const existingColumnValues = await Bloggify.services.Database.getExistingColumnValues({ model: "User", column: "name" }); ``` #### `Database.getSchema(data)` Get the schema of a model. ##### Params - `data` (Object): An object containing: - `model` (String): The model name. ##### Returns An object containing: - `model` (String): The model name. - `fields` (Array): The model fields. Each field is an object containing: - `name` (String): The field name. - `type` (String): The field type. - `field_metadata` (Object): The field metadata. - `privileges` (Object): The field privileges. - `read` (Boolean): If the field can be read. - `write` (Boolean): If the field can be written. ##### Example ```js const schema = await Bloggify.services.Database.getSchema({ model: "User" }); ``` #### `Database.bulkDelete(data)` Delete multiple records. ##### Params - `data` (Object): An object containing: - `model` (String): The model name. - `ids` (Array): The record IDs to delete. ##### Example ```js const deletedItems = await Bloggify.services.Database.bulkDelete({ model: "User", ids: [ 42, 43, 44 ] }); ``` ### `Settings` #### Events The following events are emitted: - `settings_init`: Emitted when the settings are initialized. - `settings_updated`: Emitted when the settings are updated. #### `Settings.set(data)` Set the settings. ##### Params - `data` (Object): The settings data. ##### Example ```js Bloggify.services.Settings.set({ siteName: "My Site" }); ``` #### `Settings.get()` Get the settings. ##### Returns The settings object. ##### Example ```js const settings = await Bloggify.services.Settings.get(); ``` #### `Settings.saveSettingsToDisk()` Save the settings to disk. ##### Example ```js await Bloggify.services.Settings.saveSettingsToDisk(); ``` #### `Settings.loadSettingsFromDisk()` Load the settings from disk. ##### Example ```js await Bloggify.services.Settings.loadSettingsFromDisk(); ``` ### `Util` #### `Util.handleFriendlyError(fn)` Handle friendly errors. ##### Params - `fn` (Function): The function to be executed. ##### Returns A promise that resolves to the result of the function. ##### Example ```js const result = await Bloggify.services.Util.handleFriendlyError(() => { // ... }); ``` ## Actions Below are the available actions. These actions are used in the `app/actions` directory. ### `db` – Database Actions #### `db.query` Queries a model. The body should contain the following fields: - `model` (string): The model name. - `page` (number): The page number. - `perPage` (number): The number of items per page. - `search` (string): The search term. - `sorting` (object): The sort object. - `where` (object): The where object. - `include` (array): The included models. ```js const Actions = require("bloggify/actions") Actions.post("db.query", { model: "User", page: 1, perPage: 10, search: "John", where: { age: { $gt: 42 } }, }) ``` #### `db.save` Saves a record. The body should contain the following fields: - `model` (string): The model name. - `data` (object): The data to save. If the `id` is present, the record will be updated. Otherwise, it will be created. ```js const Actions = require("bloggify/actions") Actions.post("db.save", { model: "User", data: { name: "John", age: 42 } }) ``` #### `db.getExistingColumnValues` Gets the existing column values uniquely. The body should contain the following fields: - `model` (string): The model name. - `column` (string): The column name. ```js const Actions = require("bloggify/actions") Actions.post("db.getExistingColumnValues", { model: "User", column: "name" }) ``` #### `db.delete` Deletes a record. The body should contain the following fields: - `model` (string): The model name. - `id` (number): The record ID. ```js const Actions = require("bloggify/actions") Actions.post("db.delete", { model: "User", id: 42 }) ``` #### `db.bulkDelete` Deletes multiple records. The body should contain the following fields: - `model` (string): The model name. - `ids` (array): The record IDs. ```js const Actions = require("bloggify/actions") Actions.post("db.bulkDelete", { model: "User", ids: [ 42, 43, 44 ] }) ``` #### `db.getSchema` Gets the schema for a model. The body should contain the following fields: - `model` (string): The model name. ```js const Actions = require("bloggify/actions") Actions.post("db.getSchema", { model: "User" }) ``` #### `db.exportCsv` Exports a model to a CSV file. The body should contain the following fields: - `model` (string): The model name. The form should post the data to the `Actions.url("db.exportCsv")` route and the CSV file will be downloaded. #### `db.importCsv` Imports a CSV file. The body should contain the following fields: - `model` (string): The model name. - `file` (file): The CSV file. ### `files` – File Actions #### `files.fileUpload` Uploads a file. The body should contain the following fields: - `file` (file): The file to upload. #### `files.fileList` Lists files. ```js Actions.get("files.fileList").then(files => { // ... }) ``` #### `files.fileDelete` Deletes a file. The body should contain the following fields: - `id` (string): The file ID. #### `files.fileFetch` Fetches a file. The body should contain the following fields: - `id` (string): The file ID, or - `path` (string): The file path. ### `settings` – Settings Actions #### `settings.get` Gets the settings. #### `settings.set` Sets the settings. The body should contain the following fields: - `settings` (object): The settings object. ## Authentication Handlers ### `hasRole (expectedRole, existingRoles)` Checks if the user has the expected role. ```js const { hasRole } = require("bloggify-utils/lib/auth") if (hasRole("admin", user.roles)) { // user is admin } ``` ## CSS Assets ### `application-container.css` The CSS for the application container (useful for dashboards). ```css @import "bloggify-utils/lib/assets/css/application-container.css"; ``` ### Geolocation Utilities #### `countries.js` The list of countries. ```js const { // All countries COUNTRIES, // EU countries EU_COUNTRIES, // Check if a country is a certain country countryIs } = require("bloggify-utils/lib/geo/countries") if (countryIs("US", "USA")) { // check aliases } if (countryIs("Romania", "RO")) { // check aliases } ``` ## React Components ### `createRoot(App)` Creates a root component. #### Params - `App` (React Component): The root component to render. #### Example ```jsx const createRoot = require("bloggify-utils/lib/react/create-root"); const App = () => { return <div>Hello World</div>; }; createRoot(App); ``` ### CreatableSelect Creates a select component that allows creating new options. #### Params - `value` (String): The current value. - `onChange` (Function): The change handler. - `schemaField` (Object): The schema field. - `record` (Object): The current record. - `disabled` (Boolean): If the component is disabled. - `model` (String): The model name. #### Example ```jsx <CreatableSelect value={value} onChange={onChange} schemaField={schemaField} record={record} disabled={disabled} model="User" /> ``` ### DashboardCard A dashboard card component. #### Params - `title` (String): The card title. - `image` (String): The card image. - `link` (String): The card link. - other props to be passed to the card. #### Example ```jsx <DashboardCard title="My Card" image="/path/to/image.jpg" link="/path/to/page" /> ``` ### EntityToChoose A component to choose an entity. #### Params - `model` (String): The model name. - `onChange` (Function): The change handler. - `idField` (String): The ID field. - `value` (String): The current value. - `filterQuery` (Function): The filter query. - `disabled` (Boolean): If the component is disabled. - `formatLabel` (Function): The label formatter. #### Example ```jsx <EntityToChoose model="User" onChange={onChange} idField="id" value={value} filterQuery={filterQuery} disabled={disabled} formatLabel={formatLabel} /> ``` ### FileInput A file input component. #### Params - `label` (String): The label. - `uploadAction` (String): The upload action. - `fileUploaded` (Function): The file uploaded handler. - `mediaPath` (String): The media path. #### Example ```jsx <FileInput label="Upload a file" uploadAction={Actions.url("files.fileUpload")} fileUploaded={data => console.log(data)} mediaPath="/media" /> ``` ### IframeWebView An iframe web view component. #### Params - `content` (String): The content. #### Example ```jsx <IframeWebView content="<h1>Hello World</h1>" /> ``` ### ModelTable A table component for a model. #### Params - `model` (String): The model name. - `tableColumns` (Array): The table columns to display. - `listSlug` (String): The list slug. - `defaultSorting` (Array): The default sorting. - `bulkDelete` (Boolean): If bulk delete is enabled. - `onRowSelectionChange` (Function): The row selection change handler. - `where` (Object): The where condition. - `include` (Array): The included models. - `isLinkedTable` (Boolean): If the table is linked. #### Example ```jsx <ModelTable model="User" tableColumns={[ "name", "age", ]} listSlug="users" defaultSorting={[{ id: "name", desc: true }]} bulkDelete onRowSelectionChange={selectedRows => console.log(selectedRows)} where={{ age: { $gt: 42 } }} include={[ "Role" ]} isLinkedTable /> ``` ### Page Alerts A component to display page alerts. #### Example ```jsx const { PageAlert, AccessDeniedAlert, NotImplementedAlert } = require("bloggify-utils/lib/react/page-alerts"); <PageAlert title="Title" description="Description" /> <AccessDeniedAlert /> <NotImplementedAlert /> ``` ### Spinner A spinner component. #### Example ```jsx const { Spinner } = require("bloggify-utils/lib/react/spinner"); <Spinner /> ``` ### UserToChoose A component to choose a user. #### Params All that the `EntityToChoose` component accepts. #### Example ```jsx <UserToChoose onChange={onChange} disabled={disabled} formatLabel={formatLabel} /> ``` ### UniversalCrudRecord A universal CRUD record component. #### Params - `model` (String): The model name. - `listSlug` (String): The list slug. - `renderer` (Function): The renderer function. Default: `defaultRenderer`. - `allowedFields` (Array): The allowed fields. - `EditRecordForm` (React Component): The edit record form component. Default: `EditRecordForm`. - `viewTitle` (String): The view title. - `customButtonGroup` (React Component): The custom button group. - `availableControls` (Array): The available controls. Default: `["create", "edit", "delete", "export_csv", "import_csv"]`. - `isLinkedTable` (Boolean): If the table is linked. #### Example ```jsx <UniversalCrudRecord model="User" listSlug="users" renderer={record => <div>{record.name}</div>} allowedFields={[ "name", "age" ]} EditRecordForm={EditRecordForm} viewTitle="Users" customButtonGroup={CustomButtonGroup} availableControls={[ "create", "edit", "delete", "export_csv", "import_csv" ]} /> ``` ### UniversalModelTable A universal model table component. #### Params - `model` (String): The model name. - `listTitle` (String): The list title. - `listSlug` (String): The list slug. - `afterHeader` (React Component): The after header component. - `availableControls` (Array): The available controls. Default: `["create", "edit", "delete", "export_csv", "import_csv"]`. - `isLinkedTable` (Boolean): If the table is linked. #### Example ```jsx <UniversalModelPage model="Section" listTitle="Sections" listSlug="sections" viewTitle={c => <span>Section {c ? <strong>{c.full_name}</strong> : null}</span>} tableColumns={[ { header: "Name", accessorKey: "full_name", cell: s => <SectionBadge section={s.row.original} /> }, { header: "Status", accessorKey: "status" }, { header: "Year", accessorKey: "year" }, ]} linkedTables={[ { content: (c) => <SectionCourseworkEditor section={c} />, }, { model: "Coursework", listTitle: "Coursework", listSlug: "coursework", viewTitle: (c) => <span></span>, tableColumns: [ { header: "Name", accessorKey: "coursework.name", cell: s => { return <LinkContainer to={`/dashboard/section-coursework/${s.row.original.id}`}> <a>{s.row.original.coursework.name}</a> </LinkContainer> } }, { header: "Description", accessorKey: "coursework.description" }, { header: "Value", accessorKey: "value" }, { header: "Actions", accessorKey: "section_coursework", cell: s => { return <ButtonToolbar> <ButtonGroup className="me-2"> <LinkContainer to={{ pathname: "/dashboard/instructor-tools/grade-coursework", search: `?sectionCourseworkId=${s.row.original.id}&sectionId=${s.row.original.section_id}` }} > <Button bsStyle="primary" size="sm"> <i className="fa fa-pencil" /> Grade </Button> </LinkContainer> </ButtonGroup> <SectionCourseworkGradeProgress sectionCoursework={s.row.original} /> </ButtonToolbar> } }, ], include: [{ model: "Coursework", as: "coursework", }], through: "SectionCoursework", filter: s => ({ section_id: s.id }), availableControls: [] }, { model: "Student", listTitle: "Students", listSlug: "students", viewTitle: (c) => <span></span>, tableColumns: [ { header: "Number", accessorKey: "student.number", cell: s => <UserBadge user={s.row.original.student} /> }, { header: "Name", accessorKey: "student.full_name", cell: s => <LinkContainer to={`/dashboard/student-attended-sections/${s.row.original.id}`}><a>{s.row.original.student.full_name}</a></LinkContainer> }, { header: "Email", accessorKey: "student.email" }, { header: "Current Grade", accessorKey: "grade", cell: s => s.row.original.grade ? `${s.row.original.grade} / 100` : "N/A" } ], include: [{ model: "User", as: "student", }], through: "StudentAttendedSection", filter: s => ({ section_id: s.id }), availableControls: [{ content: () => { if (!hasRole(["ADMIN", "SUPERADMIN"], BloggifyPage.user.roles)) { return null } const sectionId = location.pathname.split("/").pop() return <a href={`/dashboard/student-attended-sections/new?data.section_id=${sectionId}`} target="_blank"> <Button variant="primary" className="ml-2"> <i className="fas fa-plus-circle"></i> Add Student </Button> </a> } }] } ]} {...otherProps} /> ```