diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index 0e145d9507..bc40f586cf 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -1399,6 +1399,7 @@ class Core { boolean seen = jargs.optBoolean(1); boolean unflag = jargs.optBoolean(3); boolean delete = jargs.optBoolean(4); + boolean create = jargs.optBoolean(5); Flags flags = ifolder.getPermanentFlags(); @@ -1411,6 +1412,20 @@ class Core { if (!target.selectable) throw new IllegalArgumentException("not selectable type=" + target.type); + if (create) { + Folder icreate = istore.getFolder(target.name); + if (!icreate.exists()) { + ((IMAPFolder) ifolder).doCommand(new IMAPFolder.ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol protocol) throws ProtocolException { + protocol.create(target.name); + return null; + } + }); + ifolder.setSubscribed(true); + } + } + // De-classify if (!copy && !EntityFolder.TRASH.equals(target.type) && @@ -2625,6 +2640,8 @@ class Core { folder.download = parent.download; folder.auto_classify_source = parent.auto_classify_source; folder.auto_classify_target = parent.auto_classify_target; + folder.sync_days = parent.sync_days; + folder.keep_days = parent.keep_days; folder.unified = parent.unified; folder.navigation = parent.navigation; folder.notify = parent.notify; @@ -2845,7 +2862,7 @@ class Core { } } - private static void onRule(Context context, JSONArray jargs, EntityMessage message) throws JSONException, IOException, AddressException { + private static void onRule(Context context, JSONArray jargs, EntityMessage message) throws JSONException, MessagingException { // Download message body DB db = DB.getInstance(context); diff --git a/app/src/main/java/eu/faircode/email/EntityRule.java b/app/src/main/java/eu/faircode/email/EntityRule.java index 9b0ff7627c..73821d850f 100644 --- a/app/src/main/java/eu/faircode/email/EntityRule.java +++ b/app/src/main/java/eu/faircode/email/EntityRule.java @@ -609,18 +609,71 @@ public class EntityRule { private boolean onActionMove(Context context, EntityMessage message, JSONObject jargs) { long target = jargs.optLong("target", -1); + String create = jargs.optString("create"); boolean seen = jargs.optBoolean("seen"); boolean thread = jargs.optBoolean("thread"); DB db = DB.getInstance(context); + EntityFolder folder = db.folder().getFolder(target); if (folder == null) throw new IllegalArgumentException("Rule move to folder not found name=" + name); + if (!TextUtils.isEmpty(create)) { + Calendar calendar = Calendar.getInstance(); + String year = String.format(Locale.ROOT, "%04d", calendar.get(Calendar.YEAR)); + String month = String.format(Locale.ROOT, "%02d", calendar.get(Calendar.MONTH) + 1); + + create = create.replace("$year$", year); + create = create.replace("$month$", month); + + String domain = ""; + if (message.from != null && + message.from.length > 0 && + message.from[0] instanceof InternetAddress) { + InternetAddress from = (InternetAddress) message.from[0]; + domain = UriHelper.getEmailDomain(from.getAddress()); + } + create = create.replace("$domain$", domain); + + String name = folder.name + folder.separator + create; + EntityFolder created = db.folder().getFolderByName(message.account, name); + if (created == null) { + created = new EntityFolder(); + created.tbc = true; + created.account = folder.account; + created.namespace = folder.namespace; + created.separator = folder.separator; + created.name = name; + created.type = EntityFolder.USER; + created.subscribed = true; + created.setProperties(); + + EntityAccount account = db.account().getAccount(folder.account); + created.setSpecials(account); + + created.synchronize = folder.synchronize; + created.poll = folder.poll; + created.poll_factor = folder.poll_factor; + created.download = folder.download; + created.auto_classify_source = folder.auto_classify_source; + created.auto_classify_target = folder.auto_classify_target; + created.sync_days = folder.sync_days; + created.keep_days = folder.keep_days; + created.unified = folder.unified; + created.navigation = folder.navigation; + created.notify = folder.notify; + + created.id = db.folder().insertFolder(created); + } + target = created.id; + } + List messages = db.message().getMessagesByThread( message.account, message.thread, thread ? null : message.id, message.folder); for (EntityMessage threaded : messages) - EntityOperation.queue(context, threaded, EntityOperation.MOVE, target, seen, null, true); + EntityOperation.queue(context, threaded, EntityOperation.MOVE, target, + seen, null, true, false, !TextUtils.isEmpty(create)); message.ui_hide = true; diff --git a/app/src/main/java/eu/faircode/email/FragmentRule.java b/app/src/main/java/eu/faircode/email/FragmentRule.java index b085964b80..bf49d808e4 100644 --- a/app/src/main/java/eu/faircode/email/FragmentRule.java +++ b/app/src/main/java/eu/faircode/email/FragmentRule.java @@ -136,6 +136,7 @@ public class FragmentRule extends FragmentBase { private EditText etKeyword; private Button btnFolder; + private EditText etMoveCreate; private CheckBox cbMoveSeen; private CheckBox cbMoveThread; @@ -290,6 +291,7 @@ public class FragmentRule extends FragmentBase { etKeyword = view.findViewById(R.id.etKeyword); btnFolder = view.findViewById(R.id.btnFolder); + etMoveCreate = view.findViewById(R.id.etMoveCreate); cbMoveSeen = view.findViewById(R.id.cbMoveSeen); cbMoveThread = view.findViewById(R.id.cbMoveThread); @@ -1170,6 +1172,7 @@ public class FragmentRule extends FragmentBase { long target = jaction.optLong("target", -1); showFolder(target); if (type == EntityRule.TYPE_MOVE) { + etMoveCreate.setText(jaction.optString("create")); cbMoveSeen.setChecked(jaction.optBoolean("seen")); cbMoveThread.setChecked(jaction.optBoolean("thread")); } @@ -1525,6 +1528,7 @@ public class FragmentRule extends FragmentBase { Object tag = btnFolder.getTag(); jaction.put("target", tag instanceof Long ? (long) tag : -1); if (action.type == EntityRule.TYPE_MOVE) { + jaction.put("create", etMoveCreate.getText().toString().trim()); jaction.put("seen", cbMoveSeen.isChecked()); jaction.put("thread", cbMoveThread.isChecked()); } diff --git a/app/src/main/res/layout/fragment_rule.xml b/app/src/main/res/layout/fragment_rule.xml index 15ee7b2c90..12444fef8b 100644 --- a/app/src/main/res/layout/fragment_rule.xml +++ b/app/src/main/res/layout/fragment_rule.xml @@ -786,6 +786,37 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvMoveTarget" /> + + + + + + + app:layout_constraintTop_toBottomOf="@id/tvMoveCreateRemark" /> @@ -1026,6 +1058,7 @@ android:layout_marginTop="12dp" android:text="@string/title_rule_automation_hint" android:textAppearance="@style/TextAppearance.AppCompat.Small" + android:textStyle="italic" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/etAlarmDuration" /> @@ -1036,11 +1069,11 @@ android:layout_marginTop="12dp" android:drawableStart="@drawable/twotone_warning_24" android:drawablePadding="6dp" - app:drawableTint="?attr/colorWarning" android:text="@string/title_rule_delete_hint" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textColor="?attr/colorWarning" android:textStyle="bold" + app:drawableTint="?attr/colorWarning" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tvAutomation" /> @@ -1096,7 +1129,7 @@ android:id="@+id/grpMoveProp" android:layout_width="0dp" android:layout_height="0dp" - app:constraint_referenced_ids="cbMoveSeen,cbMoveThread" /> + app:constraint_referenced_ids="tvMoveCreate,etMoveCreate,tvMoveCreateRemark,cbMoveSeen,cbMoveThread" /> Hours From the end of the time condition Folder + Create subfolder All messages in same conversation and folder Identity Reply template