diff --git a/NodeApp/src/commander/enonce/EnoncePublishCommand.ts b/NodeApp/src/commander/enonce/EnoncePublishCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..91900f62875774f4ec254e7ad1ad001e8dc99ff3
--- /dev/null
+++ b/NodeApp/src/commander/enonce/EnoncePublishCommand.ts
@@ -0,0 +1,10 @@
+import EnoncePublishUnpublishCommandBase from './EnoncePublishUnpublishCommandBase';
+
+
+class EnoncePublishCommand extends EnoncePublishUnpublishCommandBase {
+    protected commandName: string = 'publish';
+    protected publish: boolean = true;
+}
+
+
+export default new EnoncePublishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts b/NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53c98217ee70544365cbcb8b0371f0f737b436d5
--- /dev/null
+++ b/NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts
@@ -0,0 +1,83 @@
+import CommanderCommand   from '../CommanderCommand';
+import inquirer           from 'inquirer';
+import Enonce             from '../../types/Enonce';
+import chalk              from 'chalk';
+import SessionManager     from '../../managers/SessionManager';
+import ora                from 'ora';
+import DojoBackendManager from '../../managers/DojoBackendManager';
+
+
+abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
+    protected abstract publish: boolean;
+
+    protected defineCommand() {
+        this.command
+        .description('publish an enonce')
+        .argument('<name or url>', 'name or url (http/s or ssh) of the enonce')
+        .option('-f, --force', 'don\'t ask for confirmation')
+        .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(enonceNameOrUrl: string, options: { force: boolean }): Promise<void> {
+        if ( !options.force ) {
+            options.force = (await inquirer.prompt({
+                                                       type   : 'confirm',
+                                                       name   : 'force',
+                                                       message: this.publish ? 'Are you sure you want to publish this enonce?' : 'Are you sure you want to unpublish this enonce?'
+                                                   })).force;
+        }
+
+        if ( !options.force ) {
+            return;
+        }
+
+        let enonce!: Enonce | undefined;
+
+        {
+            console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
+
+            if ( !await SessionManager.testSession(true, null) ) {
+                return;
+            }
+
+            ora('Checking enonce:').start().info();
+            ora({
+                    text  : enonceNameOrUrl,
+                    indent: 4
+                }).start().info();
+            const enonceGetSpinner: ora.Ora = ora({
+                                                      text  : 'Checking if enonce exists',
+                                                      indent: 8
+                                                  }).start();
+            enonce = await DojoBackendManager.getEnonce(enonceNameOrUrl);
+            if ( !enonce ) {
+                enonceGetSpinner.fail(`The enonce doesn't exists`);
+                return;
+            }
+            enonceGetSpinner.succeed(`The enonce exists`);
+
+            const enonceCheckAccessSpinner: ora.Ora = ora({
+                                                              text  : 'Checking accesses',
+                                                              indent: 8
+                                                          }).start();
+            if ( !enonce.staff ) {
+                enonceCheckAccessSpinner.fail(`You are not in the staff of this enonce`);
+                return;
+            }
+            enonceCheckAccessSpinner.succeed(`You are in the staff of this enonce`);
+        }
+
+        {
+            console.log(chalk.cyan(`Please wait while we ${ this.publish ? 'publish' : 'unpublish' } the enonce...`));
+
+            try {
+                await DojoBackendManager.changeEnoncePublishedStatus(enonce, this.publish);
+            } catch ( error ) {
+                return;
+            }
+        }
+    }
+}
+
+
+export default EnoncePublishUnpublishCommandBase;
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts b/NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2a3e3d33e79ed1fa20c26e6563bab85d9e33748a
--- /dev/null
+++ b/NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts
@@ -0,0 +1,10 @@
+import EnoncePublishUnpublishCommandBase from './EnoncePublishUnpublishCommandBase';
+
+
+class EnonceUnpublishCommand extends EnoncePublishUnpublishCommandBase {
+    protected commandName: string = 'unpublish';
+    protected publish: boolean = false;
+}
+
+
+export default new EnonceUnpublishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts b/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
index 2ef1f50247842cb284fa33b6a0598a8ac6432912..3e637099fa76112e6fccae70a833ea7b83aefa70 100644
--- a/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
+++ b/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
@@ -1,7 +1,6 @@
 import CommanderCommand   from '../CommanderCommand';
 import chalk              from 'chalk';
 import GitlabManager      from '../../managers/GitlabManager';
-import SessionManager     from '../../managers/SessionManager';
 import GitlabUser         from '../../shared/types/Gitlab/GitlabUser';
 import Enonce             from '../../types/Enonce';
 import ora                from 'ora';
@@ -55,11 +54,10 @@ class ExerciceCreateCommand extends CommanderCommand {
                                                             text  : 'Checking if enonce is published',
                                                             indent: 4
                                                         }).start();
-            //TODO : Check if the enonce is published
-            //if ( false ) {
-            //enoncePublishedSpinner.fail(`Enonce "${ enonce.name }" isn't published`);
-            //return;
-            //}
+            if ( !enonce.published ) {
+                enoncePublishedSpinner.fail(`Enonce "${ enonce.name }" isn't published`);
+                return;
+            }
             enoncePublishedSpinner.succeed(`Enonce "${ enonce.name }" is published`);
         }
 
diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts
index a9eede7caf911ebaacf8340561aa64df179cc6b3..cb9d9eaf36b7bdcf548b6adc7abbf1fd6b552a7a 100644
--- a/NodeApp/src/managers/DojoBackendManager.ts
+++ b/NodeApp/src/managers/DojoBackendManager.ts
@@ -129,6 +129,34 @@ class DojoBackendManager {
             throw error;
         }
     }
+
+    public async changeEnoncePublishedStatus(enonce: Enonce, publish: boolean, verbose: boolean = true) {
+        const spinner: ora.Ora = ora('Changing published status...');
+
+        if ( verbose ) {
+            spinner.start();
+        }
+
+        try {
+            await axios.patch<DojoResponse<null>>(this.getApiUrl(publish ? ApiRoutes.ENONCE_PUBLISH : ApiRoutes.ENONCE_UNPUBLISH).replace('{{nameOrUrl}}', encodeURIComponent(enonce.name)), {});
+
+            if ( verbose ) {
+                spinner.succeed(`Enonce ${ enonce.name } successfully ${ publish ? 'published' : 'unpublished' }`);
+            }
+
+            return;
+        } catch ( error ) {
+            if ( verbose ) {
+                if ( error instanceof AxiosError && error.response ) {
+                    spinner.fail(`Enonce visibility change error: ${ error.response.statusText }`);
+                } else {
+                    spinner.fail(`Enonce visibility change error: unknown error`);
+                }
+            }
+
+            throw error;
+        }
+    }
 }
 
 
diff --git a/NodeApp/src/types/ApiRoutes.ts b/NodeApp/src/types/ApiRoutes.ts
index 63f492a3a0620f32b4d1c498051704416306ef4c..04c0ae614cc8d5d8e657463e92d766cc07f35db9 100644
--- a/NodeApp/src/types/ApiRoutes.ts
+++ b/NodeApp/src/types/ApiRoutes.ts
@@ -4,6 +4,8 @@ enum ApiRoutes {
     GITLAB_CHECK_TEMPLATE_ACCESS = '/gitlab/project/{{id}}/checkTemplateAccess',
     ENONCE_GET                   = '/enonces/{{nameOrUrl}}',
     ENONCE_CREATE                = '/enonces',
+    ENONCE_PUBLISH               = '/enonces/{{nameOrUrl}}/publish',
+    ENONCE_UNPUBLISH             = '/enonces/{{nameOrUrl}}/unpublish',
     EXERCICE_CREATE              = '/enonces/{{nameOrUrl}}/exercices',
 }