Answers.Body: <p>I found a simple solution: exclude elements already having an id.</p>
  <p>Here's my final code:</p>
  <pre class="lang-php prettyprint-override"><code>Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {
      /** @var Entry $entry */
      $entry = $event-&gt;sender;
  
      // Filter out existing elements
      if ($entry-&gt;id) {
          Craft::warning('Skipped existing element', 'Custom duplication');
          return;
      }
  
      // Filter out all Drafts and Revisions
      if (ElementHelper::isDraftOrRevision($entry)) {
          Craft::warning('Skipped Draft/Revision', 'Custom duplication');
          return;
      }
  
      // Filter out non-duplicates
      if (!$entry-&gt;duplicateOf) {
          Craft::warning('Skipped Non-Duplicate', 'Custom duplication');
          return;
      }
  
      // Filter out non-documents
      if (!$entry-&gt;sectionId || $entry-&gt;getSection()-&gt;handle !== 'documents') {
          Craft::warning('Skipped Non-Document', 'Custom duplication');
          return;
      }
  
      Craft::warning('Duplication customization applied', 'Custom duplication');
      $now = new DateTime('now', new DateTimeZone('UTC'));
      $entry-&gt;dateCreated = $now;
      $entry-&gt;dateUpdated = $now;
      $entry-&gt;postDate = $now;
      $entry-&gt;setFieldValue('documentsFile', null);
      $entry-&gt;enabled = false;
  });
  
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-12T19:50:49.683Z
Answers.LastActivityDate: 2021-11-12T19:50:49.683Z
Answers.UserDisplayName: nstCactus
Answers.Body: <p>If you don't mind me rearranging your code a bit, I might go with something more like this...</p>
  <pre class="lang-php prettyprint-override"><code>Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {
  
      /** @var Entry $entry */
      $entry = $event-&gt;sender;
  
      // Filter out all Drafts and Revisions
      if (ElementHelper::isDraftOrRevision($entry)) {
          Craft::$app-&gt;getSession()-&gt;setFlash('app', 'Skipped Draft/Revision');
          return;
      }
  
      // Filter out non-duplicates
      if (!$entry-&gt;duplicateOf) {
          Craft::$app-&gt;getSession()-&gt;setFlash('app', 'Skipped Non-Duplicate');
          return;
      }
  
      // Filter out missing section IDs
      if (!$entry-&gt;sectionId) {
          Craft::$app-&gt;getSession()-&gt;setFlash('app', 'Skipped Missing Section ID');
          return;
      }
  
      // Filter out non-documents
      if ($entry-&gt;getSection()-&gt;handle !== 'documents') {
          Craft::$app-&gt;getSession()-&gt;setFlash('app', 'Skipped Non-Document');
          return;
      }
  
      // ... the rest of your logic
  
  });
  </code></pre>
  <p>Most importantly, these edits spread out the <a href="https://craftcms.com/docs/3.x/extend/coding-guidelines.html#happy-paths" rel="nofollow noreferrer">happy path</a> to be a bit clearer.</p>
  <p>And as I noted on my answer to a <a href="https://craftcms.stackexchange.com/a/38398/45">related question</a>, it's important to filter out Drafts and Revisions almost immediately (unless you are explicitly trying to use them).</p>
  <p>Hope that helps!</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-11T09:10:17.173Z
Answers.Text: Thanks for your answer. I had already tried that, with no luck: it also gets executed every time I save an entry, although the **Skipped Draft/Revision** is shown.
  After digging in the log, I found out that after duplicating the document, if I edit it and press save again, the `EVENT_BEFORE_SAVE` get caled 3 times per site : the 1st & 3rd one result in **Skipped Draft/Revision** but the 2nd one executes my code (happy path)… Any ideas why?
  Provisional drafts behavior is correct though, my code isn't run.
Answers.UserDisplayName: nstCactus
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-10T19:03:59.130Z
Answers.LastActivityDate: 2021-11-10T19:03:59.130Z
Answers.UserDisplayName: Lindsey D
Body: <p>On a Craft 3.7 site, I need to make some changes when duplicating an element:</p>
  <ul>
  <li>set the <code>dateCreated</code>, <code>dateUpdate</code> &amp; <code>postDate</code> to the current date</li>
  <li>ensure the entry is disabled</li>
  <li>clear the value of an asset custom field (<code>documentsFile</code>)</li>
  </ul>
  <p>Theses actions should be executed when using:</p>
  <ul>
  <li>the <strong>Duplicate</strong> element action available when selecting entries in the element index</li>
  <li>the <strong>Save as a new entry</strong> action available in the dropdown of the Save button of an entry edit page</li>
  </ul>
  <p>I tried with the following code in a module's <code>init()</code> method:</p>
  <pre class="lang-php prettyprint-override"><code>Event::on(Entry::class, 'Entry::EVENT_BEFORE_SAVE', static function (ModelEvent $event) {
      /** @var Entry $entry */
      $entry = $event-&gt;sender;
      if (
          !$entry-&gt;duplicateOf
          || !$entry-&gt;sectionId
          || $entry-&gt;getSection()-&gt;handle !== 'documents'
      ) {
          Craft::$app-&gt;getSession()-&gt;setFlash('cp-error', 'OUT');
          return;
      }
  
      $now = new DateTime('now', new DateTimeZone('UTC'));
      $entry-&gt;dateCreated = $now;
      $entry-&gt;dateUpdated = $now;
      $entry-&gt;postDate = $now;
      $entry-&gt;setFieldValue('documentsFile', null);
      $entry-&gt;enabled = false;
  });
  </code></pre>
  <p>It works a little too well: it gets executed everytime I save a draft (and maybe even when Craft saves a provisional draft).</p>
  <p>I'm aware of the <code>$entry-&gt;firstSave</code> property and of the <code>ElementHelper::isDraftOrRevision($entry)</code> method, but I'm not sure how to assemble everything to achieve what I want. The whole entry publication workflow seems really complex.</p>
  <p>What is the reliable way to determine when an element is being duplicated?</p>
  
Tags: <craft3><modules>
Title: How to reliably determine if an entry is being duplicated?
ThreadText: # How to reliably determine if an entry is being duplicated?
  
  (Asked by: username_0 on 2021-11-10)
  
  On a Craft 3.7 site, I need to make some changes when duplicating an element:
  
  * set the `dateCreated`, `dateUpdate` & `postDate` to the current date
  * ensure the entry is disabled
  * clear the value of an asset custom field (`documentsFile`)
  
  Theses actions should be executed when using:
  
  * the **Duplicate** element action available when selecting entries in the element index
  * the **Save as a new entry** action available in the dropdown of the Save button of an entry edit page
  
  I tried with the following code in a module's `init()` method:
  
  ```php
  Event::on(Entry::class, 'Entry::EVENT_BEFORE_SAVE', static function (ModelEvent $event) {
      /** @var Entry $entry */
      $entry = $event->sender;
      if (
          !$entry->duplicateOf
          || !$entry->sectionId
          || $entry->getSection()->handle !== 'documents'
      ) {
          Craft::$app->getSession()->setFlash('cp-error', 'OUT');
          return;
      }
  
      $now = new DateTime('now', new DateTimeZone('UTC'));
      $entry->dateCreated = $now;
      $entry->dateUpdated = $now;
      $entry->postDate = $now;
      $entry->setFieldValue('documentsFile', null);
      $entry->enabled = false;
  });
  
  ```
  
  It works a little too well: it gets executed everytime I save a draft (and maybe even when Craft saves a provisional draft).
  
  I'm aware of the `$entry->firstSave` property and of the `ElementHelper::isDraftOrRevision($entry)` method, but I'm not sure how to assemble everything to achieve what I want. The whole entry publication workflow seems really complex.
  
  What is the reliable way to determine when an element is being duplicated?
  
  **Tags:** craft3, modules
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2021-11-12. Score: 1 | Accepted answer
  
  I found a simple solution: exclude elements already having an id.
  
  Here's my final code:
  
  ```php
  Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {
      /** @var Entry $entry */
      $entry = $event->sender;
  
      // Filter out existing elements
      if ($entry->id) {
          Craft::warning('Skipped existing element', 'Custom duplication');
          return;
      }
  
      // Filter out all Drafts and Revisions
      if (ElementHelper::isDraftOrRevision($entry)) {
          Craft::warning('Skipped Draft/Revision', 'Custom duplication');
          return;
      }
  
      // Filter out non-duplicates
      if (!$entry->duplicateOf) {
          Craft::warning('Skipped Non-Duplicate', 'Custom duplication');
          return;
      }
  
      // Filter out non-documents
      if (!$entry->sectionId || $entry->getSection()->handle !== 'documents') {
          Craft::warning('Skipped Non-Document', 'Custom duplication');
          return;
      }
  
      Craft::warning('Duplication customization applied', 'Custom duplication');
      $now = new DateTime('now', new DateTimeZone('UTC'));
      $entry->dateCreated = $now;
      $entry->dateUpdated = $now;
      $entry->postDate = $now;
      $entry->setFieldValue('documentsFile', null);
      $entry->enabled = false;
  });
  
  ```
  
  
  ---
  
  ### Answer by username_1 on 2021-11-10. Score: 3
  
  If you don't mind me rearranging your code a bit, I might go with something more like this...
  
  ```php
  Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {
  
      /** @var Entry $entry */
      $entry = $event->sender;
  
      // Filter out all Drafts and Revisions
      if (ElementHelper::isDraftOrRevision($entry)) {
          Craft::$app->getSession()->setFlash('app', 'Skipped Draft/Revision');
          return;
      }
  
      // Filter out non-duplicates
      if (!$entry->duplicateOf) {
          Craft::$app->getSession()->setFlash('app', 'Skipped Non-Duplicate');
          return;
      }
  
      // Filter out missing section IDs
      if (!$entry->sectionId) {
          Craft::$app->getSession()->setFlash('app', 'Skipped Missing Section ID');
          return;
      }
  
      // Filter out non-documents
      if ($entry->getSection()->handle !== 'documents') {
          Craft::$app->getSession()->setFlash('app', 'Skipped Non-Document');
          return;
      }
  
      // ... the rest of your logic
  
  });
  
  ```
  
  Most importantly, these edits spread out the [happy path](https://craftcms.com/docs/3.x/extend/coding-guidelines.html#happy-paths) to be a bit clearer.
  
  And as I noted on my answer to a [related question](https://craftcms.stackexchange.com/a/38398/45), it's important to filter out Drafts and Revisions almost immediately (unless you are explicitly trying to use them).
  
  Hope that helps!
  
  > **Comments:**
  > **username_0**: Thanks for your answer. I had already tried that, with no luck: it also gets executed every time I save an entry, although the **Skipped Draft/Revision** is shown. After digging in the log, I found out that after duplicating the document, if I edit it and press save again, the `EVENT_BEFORE_SAVE` get caled 3 times per site : the 1st & 3rd one result in **Skipped Draft/Revision** but the 2nd one executes my code (happy path)… Any ideas why? Provisional drafts behavior is correct though, my code isn't run.  
  
  ---
  
  