Skip to content

Commit

Permalink
imp: trash
Browse files Browse the repository at this point in the history
  • Loading branch information
Tardo committed Jun 22, 2024
1 parent 63aa6b3 commit 4f772be
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 147 deletions.
100 changes: 45 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ The BFG10k for Odoo developers
All the power of Odoo json-rpc in a really easy way!
</p>

This web extension adds a terminal-like to control Odoo (11 to 17). All
implemented commands use the tools provided by the Odoo framework. An unwavering
policy when developing this extension is to not modify or alter in any way the
Odoo classes. This sometimes results in certain commands having
reduced/increased capabilities depending on the Odoo version.
This web extension adds a terminal-like to control Odoo (11 to 17). All implemented commands use the tools provided by
the Odoo framework. An unwavering policy when developing this extension is to not modify or alter in any way the Odoo
classes. This sometimes results in certain commands having reduced/increased capabilities depending on the Odoo version.

The terminal is fully initialized when it is first opened after loading the
page. The time overhead for using this extension is ~24ms.
The terminal is fully initialized when it is first opened after loading the page. The time overhead for using this
extension is ~24ms.

**Downloads**

Expand All @@ -38,12 +36,10 @@ page. The time overhead for using this extension is ~24ms.

# Usage

When you visit a Odoo website, the browser action icon of the extension turn to
enabled state. This indicates that the extension is ready to use in the current
page.
When you visit a Odoo website, the browser action icon of the extension turn to enabled state. This indicates that the
extension is ready to use in the current page.

Few commands aren't available on the frontend, use command 'help' to know the
available commands.
Few commands aren't available on the frontend, use command 'help' to know the available commands.

You can toggle terminal using one of these options:

Expand All @@ -64,36 +60,45 @@ You can toggle terminal using one of these options:
| Install module | `install -m mymodule` |
| Create alias | `alias -n myalias -c "print 'My name is: $1'"` |

> Notice that a list is an string of values separated by commas. Example: "5,
> 15, 8" (quotes included) or can use array notation [5, 15, 8]
> Notice that a list is an string of values separated by commas. Example: "5, 15, 8" (quotes included) or can use array
> notation [5, 15, 8]
> Notice that can call commands without 'named arguments', for example:
> `create res.partner {name: 'Hipcut', street: 'Mystery street'}`. The rule is
> that 'unnamed arguments' fill values following the order of the command
> arguments definition. So mix 'unnamed' with 'named' arguments can be done as
> long as the order is maintained.
> `create res.partner {name: 'Hipcut', street: 'Mystery street'}`. The rule is that 'unnamed arguments' fill values
> following the order of the command arguments definition. So mix 'unnamed' with 'named' arguments can be done as long
> as the order is maintained.
## Notes

- This extension have a "preferences" page where you can add commands to run on
every session. This is useful for example to load a remote script to extend
the 'terminal' features or declare custom aliases.
- This extension uses an internal context to extend the 'user context'. This
'terminal context' has by default the key 'active_test' = false (see issue #14
to get more information). This context only affects to terminal operations.
- The maximum buffered screen lines is set to 750. So, you can't see more than
749 records in the same query. This is necessary to avoid have a lot of
nodes... One of the problems of use HTML elements to render the output :/
- This extension have a "preferences" page where you can add commands to run on every session. This is useful for
example to load a remote script to extend the 'terminal' features or declare custom aliases.
- This extension uses an internal context to extend the 'user context'. This 'terminal context' has by default the key
'active_test' = false (see issue #14 to get more information). This context only affects to terminal operations.
- The maximum buffered screen lines is set to 750. So, you can't see more than 749 records in the same query. This is
necessary to avoid have a lot of nodes... One of the problems of use HTML elements to render the output :/
- Can remap preferred key combination at chrome://extensions/shortcuts

---

## Advance Usage

#### + HELPERS

The following are available:

| Name | Description |
| ------ | ----------------------------- |
| $$RMOD | Returns the active model name |
| $$RID | Returns the active record id |

Examples:

- `search $$AMOD`
- `write $$AMOD $$UID {name: 'The new name'}`

#### + Recordsets

`search`, `read` and `create` commands returns `recordsets`. Can use them to
write values with `commit` command.
`search`, `read` and `create` commands returns `recordsets`. Can use them to write values with `commit` command.

Example:

Expand All @@ -112,34 +117,17 @@ $new_rec = (create res.partner {name: 'The test'})
print $new_rec
```

#### + Positional replacements for aliases

You can define aliases to call commands with predefined values. This command can
use positional replacements.

The anatomy of a positional replacement is: `$num[default_value]` or `$num`

For example:

- First positional replacement (without default value = empty):
`alias -n my_alias -c "print -m 'Hello, $1'"`
- Fist position replacement with default value 'world':
`alias -n my_alias -c "print -m 'Hello, $1[world]'"`
- A somewhat more complex:
`alias -n search_mod -c "search -m ir.module.module -f display_name -d [[name, =, '$1'], [state, =, '$2[installed]']]"`

#### + Nested Calls

You can execute "commands" to use the result in a new command call. The syntax
of 'nested calls' looks like `(command)`.
You can execute "commands" to use the result in a new command call. The syntax of 'nested calls' looks like `(command)`.

For example: `read -m res.users -i (search -m res.users -f id)[0]['id']` or
`read -m res.users -i (search -m res.users -f id)['ids']`

#### + Massive operations
#### + Loops

Massive operations are possible using the command `for loops`. Print to screen is a
expensive task, consider use the keyword `silent` to increase the performance.
Massive operations are possible using the command `for loops`. Print to screen is a expensive task, consider use the
keyword `silent` to increase the performance.

Examples:

Expand All @@ -154,7 +142,7 @@ Can use the command 'genfile' to create a file object that can be sent via post.

Example:

- `post /web/binary/upload_attachment -d {callback: '', model: 'res.partner', id: 1, ufile: (genfile)}`
- `post '/web/binary/upload_attachment' -d {callback: '', model: 'res.partner', id: 1, ufile: (genfile)}`

#### + Websockets

Expand All @@ -178,14 +166,16 @@ Examples:
#### + If, Else

Example:
- `if ((gen int 0 8) > 2) { print 'Yes!' } else { print 'No' }`
- `$num = (gen int 0 8); if ($num > 2) { print 'Yes! ' + $num + ' > 2' } else { print 'No... ' + $num + ' <= 2' }`

- `if ((gen int 0 8) > 2) { print 'Yes!' } else { print 'No' }`
- `$num = (gen int 0 8); if ($num > 2) { print 'Yes! ' + $num + ' > 2' } else { print 'No... ' + $num + ' <= 2' }`

#### + Functions

Example:
- `function myfun(user_name) { print "Hello, " + $user_name }; myfun 'world!'`
- `$myfun = function (user_name) { print "Hello, " + $user_name }; $$myfun 'world!'`

- `function myfun(user_name) { print "Hello, " + $user_name }; myfun 'world!'`
- `$myfun = function (user_name) { print "Hello, " + $user_name }; $$myfun 'world!'`

---

Expand Down
13 changes: 13 additions & 0 deletions src/js/page/odoo/terminal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ export default class OdooTerminal extends Terminal {
this.screen.print(renderWelcome(this.VERSION));
}

/**
* @override
*/
async start(): Promise<> {
await super.start();
// Helpers
const helpers = `
$RMOD=function(){return (url -s hash -k model)}
$RID=function(){return (url -s hash -k id)}
`;
await this.execute(helpers, false, true);
}

/**
* @override
*/
Expand Down
31 changes: 28 additions & 3 deletions src/js/page/terminal/terminal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export default class Terminal {
}
}

start(): void {
async start(): Promise<> {
if (!this.#wasLoaded) {
throw new Error(i18n.t('terminal.error.notLoaded', 'Terminal not loaded'));
}
Expand Down Expand Up @@ -261,9 +261,25 @@ export default class Terminal {
}
}

async execute(code: string, store: boolean = true, silent: boolean = false): Promise<mixed> {
await this.#wakeUp();
#parseAlias(aliases: {[string]: string}, cmd_name: string, args: $ReadOnlyArray<string>): string | void {
let alias_cmd = aliases[cmd_name];
if (alias_cmd) {
const params_len = args.length;
let index = 0;
while (index < params_len) {
const re = new RegExp(`\\$${Number(index) + 1}(?:\\[[^\\]]+\\])?`, 'g');
alias_cmd = alias_cmd.replaceAll(re, args[index]);
++index;
}
alias_cmd = alias_cmd.replaceAll(/\$\d+(?:\[([^\]]+)\])?/g, (_, group) => {
return group || '';
});
return alias_cmd;
}
return undefined;
}

async execute(code: string, store: boolean = true, silent: boolean = false): Promise<mixed> {
if (!silent) {
this.screen.printCommand(code);
}
Expand Down Expand Up @@ -298,6 +314,15 @@ export default class Terminal {
}

async #invokeExternalCommand(meta: JobMetaInfo): Promise<mixed> {
const aliases = getStorageLocalItem('terminal_aliases', {});
if (meta.info.cmdName in aliases) {
const alias_cmd = this.#parseAlias(aliases, meta.info.cmdName, meta.info.args);
return this.#shell.eval(alias_cmd || "", {
silent: meta.silent,
aliases: aliases,
});
}

const call_ctx = {
screen: new Proxy(this.screen, {...ScreenCommandHandler, silent: meta.silent}),
meta: meta,
Expand Down
18 changes: 16 additions & 2 deletions src/js/page/tests/test_common.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ export default class TestCommon extends TerminalTestSuite {
async test_json() {
const res =
await this.terminal.execute(
"json -e /web_editor/get_assets_editor_resources -d {key:'web.assets_backend'}",
"json -e '/web_editor/get_assets_editor_resources' -d {key:'web.assets_backend'}",
false,
true,
);
Expand Down Expand Up @@ -363,9 +363,23 @@ export default class TestCommon extends TerminalTestSuite {
}

async test_url() {
await this.terminal.execute('view res.partner 1', false, true);
let res = await this.terminal.execute('url', false, true);
this.assertNotEmpty(res);
res = await this.terminal.execute('url -s hash -k menu_id', false, true);
res = await this.terminal.execute('url -s hash -k model', false, true);
this.assertNotEmpty(res);
}

async test_helpers() {
await this.terminal.execute('view res.partner 1', false, true);
let res = await this.terminal.execute('$$RMOD', false, true);
this.assertEqual(res, 'res.partner');
res = await this.terminal.execute('$$RID', false, true);
this.assertEqual(res, '1');
await this.terminal.execute('view res.partner 3', false, true);
res = await this.terminal.execute('$$RMOD', false, true);
this.assertEqual(res, 'res.partner');
res = await this.terminal.execute('$$RID', false, true);
this.assertEqual(res, '3');
}
}
2 changes: 1 addition & 1 deletion src/js/page/tests/test_core.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default class TestCore extends TerminalTestSuite {
// This.assertEmpty(res);
res = (
await this.terminal.execute('alias -n test -c "print -m \'This is a test! $1 ($2[Nothing])\'"', false, true)
)[0];
);
this.assertIn(res, 'test');
res = await this.terminal.execute('test Foo "Bar Space"', false, true);
this.assertEqual(res, 'This is a test! Foo (Bar Space)');
Expand Down
39 changes: 19 additions & 20 deletions src/js/page/trash/interpreter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -310,25 +310,25 @@ export default class Interpreter {
token_san === SYMBOLS.ITEM_DELIMITER
) {
ttype = LEXER.Delimiter;
} else if (token_san === SYMBOLS.AND) {
} else if (token_san === SYMBOLS.AND && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.And;
} else if (token_san === SYMBOLS.OR) {
} else if (token_san === SYMBOLS.OR && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.Or;
} else if (token_san === SYMBOLS.NOT) {
} else if (token_san === SYMBOLS.NOT && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.Not;
} else if (token_san === SYMBOLS.EQUAL) {
} else if (token_san === SYMBOLS.EQUAL && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.Equal;
} else if (token_san === SYMBOLS.NOT_EQUAL) {
} else if (token_san === SYMBOLS.NOT_EQUAL && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.NotEqual;
} else if (token_san === SYMBOLS.GREATER_THAN_OPEN) {
} else if (token_san === SYMBOLS.GREATER_THAN_OPEN && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.GreaterThanOpen;
} else if (token_san === SYMBOLS.LESS_THAN_OPEN) {
} else if (token_san === SYMBOLS.LESS_THAN_OPEN && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.LessThanOpen;
} else if (token_san === SYMBOLS.GREATER_THAN_CLOSED) {
} else if (token_san === SYMBOLS.GREATER_THAN_CLOSED && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.GreaterThanClosed;
} else if (token_san === SYMBOLS.LESS_THAN_CLOSED) {
} else if (token_san === SYMBOLS.LESS_THAN_CLOSED && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.LessThanClosed;
} else if (token_san === SYMBOLS.ASSIGNMENT) {
} else if (token_san === SYMBOLS.ASSIGNMENT && (!prev_token_info_no_space || prev_token_info_no_space[0] !== LEXER.Delimiter)) {
ttype = LEXER.Assignment;
} else if (token_san[0] === SYMBOLS.ARRAY_START && token_san.at(-1) === SYMBOLS.ARRAY_END) {
token_san = token_san.substr(1, token_san.length - 2);
Expand Down Expand Up @@ -387,12 +387,6 @@ export default class Interpreter {
} else if (token_san_lower === KEYWORDS.SILENT) {
ttype = LEXER.Silent;
} else if (token_san === SYMBOLS.ADD) {
// if (prev_token_info && prev_token_info[0] === LEXER.Space && (next_char === SYMBOLS.VARIABLE || !isNaN(Number(next_char)) || (prev_token_info_no_space && LEXER_MATH_OPER.includes(prev_token_info_no_space[0])))) {
// debugger;
// ttype = LEXER.Positive;
// } else {
// ttype = LEXER.Add;
// }
ttype = LEXER.Add;
} else if (token_san === SYMBOLS.SUBSTRACT) {
// FIXME: This is a bit crazy :)
Expand Down Expand Up @@ -502,8 +496,11 @@ export default class Interpreter {
// Name
let active_value = token_active.raw.trim();
if (active_value.startsWith(SYMBOLS.FUNCTION_ARGS_START)) {
fun_args = token_active.value.split(',')
.map(item => ([ARG.Any, [item.trim(), item.trim()], false, i18n.t('cmdFuncTrash.args.name', 'The function parameter'), undefined]: ArgDef));
active_value = token_active.value.trim();
if (active_value.length > 0) {
fun_args = active_value.split(',')
.map(item => ([ARG.Any, [item.trim(), item.trim()], false, i18n.t('cmdFuncTrash.args.name', 'The function parameter'), undefined]: ArgDef));
}
} else {
fun_name = active_value;
// Args
Expand All @@ -512,8 +509,10 @@ export default class Interpreter {
return;
}
active_value = token_active.value.trim();
fun_args = active_value.split(',')
.map(item => ([ARG.Any, [item.trim(), item.trim()], false, i18n.t('cmdFuncTrash.args.name', 'The function parameter'), undefined]: ArgDef));
if (active_value.length > 0) {
fun_args = active_value.split(',')
.map(item => ([ARG.Any, [item.trim(), item.trim()], false, i18n.t('cmdFuncTrash.args.name', 'The function parameter'), undefined]: ArgDef));
}
}

// Code
Expand Down
4 changes: 2 additions & 2 deletions src/js/page/trash/shell.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ export default class Shell {
onFinishCommand(job_index: number) {
const job_info = this.#jobs[job_index];
clearTimeout(job_info.timeout);
delete this.#jobs[job_index];
if (typeof this.#options.onFinishCommand !== 'undefined') {
this.#options.onFinishCommand(this.#jobs[job_index]);
this.#options.onFinishCommand(job_info);
}
delete this.#jobs[job_index];
}
}
Loading

0 comments on commit 4f772be

Please sign in to comment.