Skip to content

Developing

Florian Steffens edited this page Jan 16, 2024 · 21 revisions

Setup

GitHub Codespaces / VS Code devcontainer

This was added with version 0.5.1.

  • Open code spaces or the repository in VS Code to start the dev container
  • The container will automatically install all dependencies and build the app
  • Nextcloud will be installed from the master development branch and be available on a port exposed by the container

Install from source code

To build you will need to have Node.js and Composer installed.

# Clone repository into the app-directory:
cd /path/to/apps && git clone https://github.com/nextcloud/tables && cd tables`

# Install PHP dependencies: 
composer install

# Install JS dependencies: 
npm ci

# Build JavaScript for the frontend
npm run dev

# or for instand compiling
npm run watch

There is a namespace overlap between the app and some 3rd party dev-dependency. If you get errors on running for example occ commands, please remove the composer dev-dependencies like this:

# go to repository
cd /path/to/apps/tables
rm -r vendor
composer install --no-dev

General information

Column types

Tables can define their own structure by incorporating columns. A column type determines information about what kind of information is intended to store in it. Depending on this column type different attributes enrich the column such as max and min values for number columns or the selection options for a selection column.

Following column types are known:

  • TextLine
  • TextLong deprecated since v0.5.0 and NC >= 26
  • TextRich new since v0.5.0 and NC26
  • TextLink
  • Number
  • NumberStars
  • NumberProgress
  • Selection
  • SelectionMulti
  • SelectionCheck
  • Datetime
  • DatetimeDate
  • DatetimeTime

Formats

Date & time

The tables app uses a default internal format to store data. This look like the following (related to the php DateTime class):

  • Date Y-m-d example '2023-12-24'
  • Time H:i example '09:00'
  • Datetime Y-m-d H:i example '2023-12-24 12:00'

You should use this format. We also support iso date string (example 2019-09-07T15:50:00+01:00) for importing data.

Views

Starting with the version 0.6 the tables app knows views. Here are some good-to-know-facts about views:

  1. Each Table can have multiple views. Views always belong to one table.
  2. Views control permissions. If you share it, you share it with the configuration by a view. So only the selected columns will be accessible and so on.
  3. If you want to create or edit views you have to be the owner or a manager of the table.
  4. A link to a single row always needs an underlying view or the default view is used. This is needed because the view will configure which columns in what order are shown and how they are formatted etc.

Filter Views can have more than one filter, and they can be combined within groups. To simplify the UX for the user we use the following logic to handle grouping:

  • Every view can have unlimited groups.
  • Groups are combined with an OR operator.
  • Every group can have unlimited filters.
  • Filters inside a group are combined with an AND operator.

Deeper dive

Backend Details

Columns

Every column has a set attribute with the name type that indicates a kind of technical category. There might be individual fields for this type that are explicit stored in the db. Second we have a subtype, that always belongs to a main type. This can make use of the individual fields from the main type, but don't have own properties (except for inline props stored in the main type props). This way we can have components that handle all subtypes for a main type, if possible. Or we need individual components for each subtype, depending on the use case.

Example would be the main type text, which has several subtypes as line, link or rich.

Especially in the front end we use the combined type joined with a dash (-), so we get:

  • text-long
  • text-link
  • ...

It is not necessary to have a subtype, for example for plain numbers number. The combined column type is also number in that case.

Frontend Details

Columns

A column is a class with the superclass AbstractColumn. This superclass unifies common attributes such as tableID, title, etc. There are further AbstractColumn types for each main type. They currently are:

  • AbstractNumberColumn
  • AbstractDatetimeColumn
  • AbstractTextColumn
  • AbstractSelectionColumn

Finally, the single column classes extend directly the AbstractColumn (currently none) or one abstract subclass of it. They implement the logic that differs between the single column types like sort, isSearchStringFound, isFilterFound and more. Also the column type, which is stored as an attribute, is set in them. It is formatted as snake_case with dashes: E.g.

  • number for NumberColumn
  • text-long for TextLongColumn
  • ...

You can fetch all served column-types from the capabilities (since v0.5.0): curl http://admin:admin@nextcloud.local/ocs/v1.php/cloud/capabilities\?format\=json -H 'OCS-APIRequest: true' | jq .ocs.data.capabilities.tables.column_types

Specifics about the text-link column type

The text-link column holds a simple url since the first version of the tables app. Beginning with version 0.6 it is possible to insert all known resources that implement a search-provider. The tables app reuses these as described.

If you want to make use of the new resources, you have to define which search provider are allowed. These can be fetched via the search API. Write them into the textAllowedPattern comma seperated.

Example:

url,mail,calendar,search-deck-card-board

To insert a value for a row, please use an encoded json-object that should look like the following:

{
  "thumbnailUrl": "http://nextcloud.local/index.php/core/preview?fileId=7&x=32&y=32",
  "title": "photo-1503991721143-75f95ebf1e55.jpeg",
  "subline": "Files",
  "resourceUrl": "http://nextcloud.local/index.php/f/7",
  "icon": "/index.php/apps/theming/img/core/filetypes/image.svg?v=21421e36",
  "providerId": "files"
}
  • If there is no thumbnailUrl or the destination of it can't be reached, the icon will be displayed.
  • If there is no icon, a default link-icon will be displayed.
  • The subline should hold a name for the source like the source app name. (Contacts, Mail, Files, ...)

Frontend folder structure

NC-Tables-FE-Architecture

Subset #1

This subset describes a reusable structure. It will be referenced by the following parts.

You can have different mixins for this particular subset that goes into the mixed folder. Partials are small UI components that can have internal UI logic like animations, or error hints. But they are always self-contained. Interact with them via props and local emitting events. Between the different components like data layer, partials and layout pages we need some kind of glue to bring them all together. This is covered by the sections. They can fetch data, organize partials and will be used within pages once or multiple times.

modules

We have different business units to split up the hole tables project a bit. Each module handles their business inside of this folder.

We assume that if a module wants to communicate with another module, this is a common event and might be interesting for other modules as well. That's why communication across multiple modules should go over the event bus system.

Each module can have a subset structure like in the picture subset #1. Look at the general description about this subset.

modals

The modals module is a bit special. It holds all modals that can be opened and will be included in all mother component (see pages for details).

pages Pages are the layout for the main content part. It holds the navigation section, the main content parts (sections) and also the sidebar if needed.

shared Sometimes we create a component with the idea that this one could be reused all over the tables app and maybe also in other apps. Let's put them into the shared folder to indicate that these components are made to reuse. Maybe these can mature over time and might be merged into the vue library one day.

assets

The assets holds some shared static assets like icons.

components

Complete vue components that are completely independent goes in here. The components make use of the subset #1.

mixins

If you have a mixin that is not related to a specific module and might be reused, here's a good place for them. Think about typical helpers.

modals

If you have a modal that is not related to a specific module and might be reused, here's a good place for them.

utils

If you need native js utils, put it in here.

store

The store holds all data that are often reused by multiple modules. Otherwise, this data could be hold by a section.

Metadata like the actual view mode, active table ID or loading states goes in here as well.

views

This views folder is unrelated to the 'views' in tables. This contains components that are used by integration parts such as the smart picker, widget rendering etc. They can also make use of the subset #1.

Magic values

There are some points where you can use (insert) magic-values.

Magic values are variables which can be selected by the users and which get live rendered during a request. They are called magic because they will replace the values related to the context of a request. This could be the username, user ID, local time, timezone etc.

For example the "magic value" could be me, that means the program will replace this with your individual userId. This gives you the ability to create views that are filtered by the users who open this view (means depending on who is logged in).

Following magic values are known:

Name id Replacement
Me (user ID) me User ID
Me (name) my-name Display name
checked checked βœ…
unchecked unchecked ❌
stars-1 stars-1 β˜…β˜†β˜†β˜†β˜†
... ...
stars-5 stars-5 β˜…β˜…β˜…β˜…β˜…
Today datetime-date-today Today's date
Start of the year datetime-date-start-of-year
Start of the month datetime-date-start-of-month
Start of the week datetime-date-start-of-week
Now datetime-time-now Actual time
Now datetime-now Actual date and time

Views and filtering

Filter

Views and tables can filter columns by an operator for different values. Magic values can be used. They will be filtered in the frontend.

Operators

The following operators exists and are allowed for specific column types:

Operator Good for Hints
contains All Takes every value as string
begins-with String/Text
ends-with String/Text
is-equal All
is-greater-than Number/Date/Time
is-greater-than-or-equal Number/Date/Time
is-lower-than Number/Date/Time
is-lower-than-or-equal Number/Date/Time
is-empty All

Views filtering in detail

The complete filter definition for a view contains an outer array that holds all groups. And an inner array for each group that holds all the filter definitions.

example

We want to show all my tasks that are urgent or overdue. Let's assume the due date can be found in the column with id 5 and the urgent flag is stored in column with id 6. Column id 2 holds the userId who is in charge of that task.

[
  [
    {
      "columnId": 6,
      "operator": "is-equal",
      "value": "[checked]"
    },
    {
      "columnId": 2,
      "operator": "is-equal",
      "value": "[me]"
    }
  ],
  [
    {
      "columnId": 5,
      "operator": "is-lower-than",
      "value": "[datetime-date-today]"
    },
    {
      "columnId": 2,
      "operator": "is-equal",
      "value": "[me]"
    }
  ]
]

Testing

These instructions assume you're running Nextcloud locally using nextcloud-docker-dev. Ensure your Docker nextcloud container running and the Tables app is enabled.

  • To run all tests:
    make test
    
  • To run unit tests:
    make test-unit
    
  • To run integration tests:
    make test-behat
    
  • To run all e2e tests:
    make test-cypress