diff --git a/action.php b/action.php index e0b5384..6a141eb 100644 --- a/action.php +++ b/action.php @@ -190,17 +190,28 @@ switch ($VARS['action']) { $data['uid'] = $_SESSION['uid']; $database->insert('mail_lists', $data); $listid = $database->id(); - if (is_empty($VARS['cloneid']) || !$database->has("mail_lists", ['listid' => $VARS['cloneid']])) { - // Yeah, I'm copypasting. Deal with it. - } else { - $addresses = $database->select("addresses", ["email", "name"], ["listid" => $VARS['cloneid']]); - foreach ($addresses as $addr) { - $addr["listid"] = $listid; - $database->insert("addresses", $addr); - } - } } else { $database->update('mail_lists', $data, ['listid' => $VARS['listid']]); + $listid = $VARS['listid']; + } + + $emails = explode(",", $VARS['emails']); + $dbemails = $database->select('addresses', 'email', ['listid' => $listid]); + $todelete = $dbemails; + $toadd = []; + foreach ($emails as $m) { + if (!in_array($m, $dbemails)) { + $toadd[] = $m; + } + + $todelete = array_diff($todelete, [$m]); + } + + foreach ($todelete as $m) { + $database->delete('addresses', ["AND" => ['listid' => $listid, "email" => $m]]); + } + foreach ($toadd as $m) { + $database->insert('addresses', ['listid' => $listid, 'email' => $m, 'name' => '']); } returnToSender("list_saved"); case "deletelist": diff --git a/lang/en_us.php b/lang/en_us.php index 1c466ea..08845ac 100644 --- a/lang/en_us.php +++ b/lang/en_us.php @@ -86,4 +86,5 @@ define("STRINGS", [ "addresses" => "Addresses", "theme" => "Theme", "format" => "Format", + "addresses comma separated" => "Addresses (comma separated)" ]); \ No newline at end of file diff --git a/lib/getlisttable.php b/lib/getlisttable.php index 4faf024..f0b6cbe 100644 --- a/lib/getlisttable.php +++ b/lib/getlisttable.php @@ -88,6 +88,7 @@ for ($i = 0; $i < count($lists); $i++) { } $lists[$i]["username"] = $usercache[$lists[$i]['uid']]['name']; } + $lists[$i]['count'] = $database->count('addresses', ['listid' => $lists[$i]["listid"]]); } $out['lists'] = $lists; diff --git a/pages.php b/pages.php index f0e65d0..1049875 100644 --- a/pages.php +++ b/pages.php @@ -59,7 +59,11 @@ define("PAGES", [ "editlist" => [ "title" => "edit list", "navbar" => false, + "styles" => [ + "static/css/tagsinput.css" + ], "scripts" => [ + "static/js/jquery.tagsinput.min.js", "static/js/editlist.js" ], ], diff --git a/pages/editlist.php b/pages/editlist.php index 6c9a31b..13c35f3 100644 --- a/pages/editlist.php +++ b/pages/editlist.php @@ -12,6 +12,7 @@ $data = [ 'name' => '', 'id' => '' ]; +$emails = []; $editing = false; $cloning = false; @@ -30,6 +31,7 @@ if (!is_empty($VARS['id'])) { ], [ 'listid' => $VARS['id'] ])[0]; + $emails = $database->select('addresses', 'email', ['listid' => $VARS['id']]); } else { // item id is invalid, redirect to a page that won't cause an error when pressing Save header('Location: app.php?page=editlist'); @@ -63,8 +65,13 @@ if (!is_empty($VARS['id'])) { " required="required" value="" /> -
- + +
+ " />
@@ -74,9 +81,6 @@ if (!is_empty($VARS['id'])) { } ?>" /> - - - diff --git a/static/css/tagsinput.css b/static/css/tagsinput.css new file mode 100644 index 0000000..9a6daf6 --- /dev/null +++ b/static/css/tagsinput.css @@ -0,0 +1,61 @@ +/* + * From https://github.com/xoxco/jQuery-Tags-Input + * MIT License + */ +div.tagsinput { + padding: 5px; + padding-top: 10px; + padding-left: 10px; + width: 300px; + height: 100px; + overflow-y: auto; + font-size: 16px; +} + +div.tagsinput span.tag { + border: 1px solid #a5d24a; + display: block; + border-radius: 2px; + float: left; + padding: 5px; + text-decoration: none; + background-color: #4CAF50; + color: #fff; + margin-right: 8px; + margin-bottom: 8px; + box-shadow: 0 1px 4px rgba(0,0,0,0.4); + line-height: 1.5; + border-radius: 0.25rem; +} + +div.tagsinput span.tag a { + font-weight: bold; + color: #fff; + text-decoration: none; + font-size: 12px; + text-transform: uppercase; + padding-right: 3px; +} + +div.tagsinput input { + width: 100%; + margin: 0px; + border: 1px solid transparent; + padding: 5px; + background: transparent; + color: #000; + outline: 0px; + margin-right: 5px; + margin-bottom: 5px; +} + +div.tagsinput div { + display: block; + float: left; +} + +.tags_clear { + clear: both; + width: 100%; + height: 0px; +} \ No newline at end of file diff --git a/static/js/editlist.js b/static/js/editlist.js index 7e3b16d..ef9675d 100644 --- a/static/js/editlist.js +++ b/static/js/editlist.js @@ -4,4 +4,20 @@ $('#name').on('input propertychange paste', function () { $('#name_title').text($('#name').val()); +}); + +$("#emails").tagsInput({ + height: "100%", + width: "100%", + defaultText: "Click to add", + onAddTag: function (tag) { + if (! +/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(tag)) { + $("#emails").removeTag(tag); + alert(tag + " does not appear to be a valid email address."); + } else if (tag.length > 255) { + $("#emails").removeTag(tag); + alert(tag + " is too long. Email addresses must be less than 256 characters in length."); + } + } }); \ No newline at end of file diff --git a/static/js/jquery.tagsinput.min.js b/static/js/jquery.tagsinput.min.js new file mode 100644 index 0000000..fbb0c7f --- /dev/null +++ b/static/js/jquery.tagsinput.min.js @@ -0,0 +1 @@ +!function(a){var b=new Array,c=new Array;a.fn.doAutosize=function(b){var c=a(this).data("minwidth"),d=a(this).data("maxwidth"),e="",f=a(this),g=a("#"+a(this).data("tester_id"));if(e!==(e=f.val())){var h=e.replace(/&/g,"&").replace(/\s/g," ").replace(//g,">");g.html(h);var i=g.width(),j=i+b.comfortZone>=c?i+b.comfortZone:c,k=f.width(),l=k>j&&j>=c||j>c&&d>j;l&&f.width(j)}},a.fn.resetAutosize=function(b){var c=a(this).data("minwidth")||b.minInputWidth||a(this).width(),d=a(this).data("maxwidth")||b.maxInputWidth||a(this).closest(".tagsinput").width()-b.inputPadding,e=a(this),f=a("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),letterSpacing:e.css("letterSpacing"),whiteSpace:"nowrap"}),g=a(this).attr("id")+"_autosize_tester";!a("#"+g).length>0&&(f.attr("id",g),f.appendTo("body")),e.data("minwidth",c),e.data("maxwidth",d),e.data("tester_id",g),e.css("width",c)},a.fn.addTag=function(d,e){return e=jQuery.extend({focus:!1,callback:!0},e),this.each(function(){var f=a(this).attr("id"),g=a(this).val().split(b[f]);if(""==g[0]&&(g=new Array),d=jQuery.trim(d),e.unique){var h=a(this).tagExist(d);1==h&&a("#"+f+"_tag").addClass("not_valid")}else var h=!1;if(""!=d&&1!=h){if(a("").addClass("tag").append(a("").text(d).append("  "),a("",{href:"#",title:"Removing tag",text:"x"}).click(function(){return a("#"+f).removeTag(escape(d))})).insertBefore("#"+f+"_addTag"),g.push(d),a("#"+f+"_tag").val(""),e.focus?a("#"+f+"_tag").focus():a("#"+f+"_tag").blur(),a.fn.tagsInput.updateTagsField(this,g),e.callback&&c[f]&&c[f].onAddTag){var i=c[f].onAddTag;i.call(this,d)}if(c[f]&&c[f].onChange){var j=g.length,i=c[f].onChange;i.call(this,a(this),g[j-1])}}}),!1},a.fn.removeTag=function(d){return d=unescape(d),this.each(function(){var e=a(this).attr("id"),f=a(this).val().split(b[e]);for(a("#"+e+"_tagsinput .tag").remove(),str="",i=0;i=0},a.fn.importTags=function(b){var c=a(this).attr("id");a("#"+c+"_tagsinput .tag").remove(),a.fn.tagsInput.importTags(this,b)},a.fn.tagsInput=function(e){var f=jQuery.extend({interactive:!0,defaultText:"add a tag",minChars:0,width:"300px",height:"100px",autocomplete:{selectFirst:!1},hide:!0,delimiter:",",unique:!0,removeWithBackspace:!0,placeholderColor:"#666666",autosize:!0,comfortZone:20,inputPadding:12},e),g=0;return this.each(function(){if("undefined"==typeof a(this).attr("data-tagsinput-init")){a(this).attr("data-tagsinput-init",!0),f.hide&&a(this).hide();var e=a(this).attr("id");(!e||b[a(this).attr("id")])&&(e=a(this).attr("id","tags"+(new Date).getTime()+g++).attr("id"));var h=jQuery.extend({pid:e,real_input:"#"+e,holder:"#"+e+"_tagsinput",input_wrapper:"#"+e+"_addTag",fake_input:"#"+e+"_tag"},f);b[e]=h.delimiter,(f.onAddTag||f.onRemoveTag||f.onChange)&&(c[e]=new Array,c[e].onAddTag=f.onAddTag,c[e].onRemoveTag=f.onRemoveTag,c[e].onChange=f.onChange);var i='
';if(f.interactive&&(i=i+''),i+='
',a(i).insertAfter(this),a(h.holder).css("width",f.width),a(h.holder).css("min-height",f.height),a(h.holder).css("height",f.height),""!=a(h.real_input).val()&&a.fn.tagsInput.importTags(a(h.real_input),a(h.real_input).val()),f.interactive){if(a(h.fake_input).val(a(h.fake_input).attr("data-default")),a(h.fake_input).css("color",f.placeholderColor),a(h.fake_input).resetAutosize(f),a(h.holder).bind("click",h,function(b){a(b.data.fake_input).focus()}),a(h.fake_input).bind("focus",h,function(b){a(b.data.fake_input).val()==a(b.data.fake_input).attr("data-default")&&a(b.data.fake_input).val(""),a(b.data.fake_input).css("color","#000000")}),void 0!=f.autocomplete_url){autocomplete_options={source:f.autocomplete_url};for(attrname in f.autocomplete)autocomplete_options[attrname]=f.autocomplete[attrname];void 0!==jQuery.Autocompleter?(a(h.fake_input).autocomplete(f.autocomplete_url,f.autocomplete),a(h.fake_input).bind("result",h,function(b,c,d){c&&a("#"+e).addTag(c[0]+"",{focus:!0,unique:f.unique})})):void 0!==jQuery.ui.autocomplete&&(a(h.fake_input).autocomplete(autocomplete_options),a(h.fake_input).bind("autocompleteselect",h,function(b,c){return a(b.data.real_input).addTag(c.item.value,{focus:!0,unique:f.unique}),!1}))}else a(h.fake_input).bind("blur",h,function(b){var c=a(this).attr("data-default");return""!=a(b.data.fake_input).val()&&a(b.data.fake_input).val()!=c?b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length)&&a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:!0,unique:f.unique}):(a(b.data.fake_input).val(a(b.data.fake_input).attr("data-default")),a(b.data.fake_input).css("color",f.placeholderColor)),!1});a(h.fake_input).bind("keypress",h,function(b){return d(b)?(b.preventDefault(),b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length)&&a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:!0,unique:f.unique}),a(b.data.fake_input).resetAutosize(f),!1):void(b.data.autosize&&a(b.data.fake_input).doAutosize(f))}),h.removeWithBackspace&&a(h.fake_input).bind("keydown",function(b){if(8==b.keyCode&&""==a(this).val()){b.preventDefault();var c=a(this).closest(".tagsinput").find(".tag:last").text(),d=a(this).attr("id").replace(/_tag$/,"");c=c.replace(/[\s]+x$/,""),a("#"+d).removeTag(escape(c)),a(this).trigger("focus")}}),a(h.fake_input).blur(),h.unique&&a(h.fake_input).keydown(function(b){(8==b.keyCode||String.fromCharCode(b.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/))&&a(this).removeClass("not_valid")})}}}),this},a.fn.tagsInput.updateTagsField=function(c,d){var e=a(c).attr("id");a(c).val(d.join(b[e]))},a.fn.tagsInput.importTags=function(d,e){a(d).val("");var f=a(d).attr("id"),g=e.split(b[f]);for(i=0;i