using System; using System.Configuration; using System.Configuration.Provider; using Npgsql; using System.Collections.Generic; using Yavsc.Model.Blogs; using Yavsc.Model.Circles; using NpgsqlTypes; using System.Linq; namespace Npgsql.Web.Blog { /// /// Npgsql blog provider. /// public class NpgsqlBlogProvider : BlogProvider { string applicationName; string connectionString; #region implemented abstract members of BlogProvider public override void UpdatePost (BlogEntry be) { // TODO Tags and rate should not be ignored UpdatePost (be.Id, be.Title, be.Content, be.Visible, be.AllowedCircles); } /// /// Note the specified postid and note. /// /// Postid. /// rate. public override void Rate (long postid, int rate) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "update blog set rate = :rate where _id = :pid"; cmd.Parameters.AddWithValue ("rate", rate); cmd.Parameters.AddWithValue ("pid", postid); cnx.Open (); cmd.ExecuteNonQuery (); cnx.Close (); } } /// /// Tag the specified post by identifier /// using the given tag. /// /// Postid. /// Tag name. public override long Tag (long postid, string tagname) { long tid = GetOrCreateTagId (tagname); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "INSERT INTO tagged (tagid,postid) VALUES (:tid,:pid)"; cmd.Parameters.AddWithValue ("tid", tid); cmd.Parameters.AddWithValue ("pid", postid); cnx.Open (); cmd.ExecuteNonQuery (); return tid; } } /// /// Uns the tag. /// /// Postid. /// Tagid. /// Name. override public void Untag (long postid, string name) { Untag (postid, GetTagId (name)); } /// /// Uns the tag. /// /// Postid. /// Tagid. /// Tid. override public void Untag (long postid, long tid) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "DELETE FROM tagged WHERE postid = :pid AND tagid = :tid"; cmd.Parameters.AddWithValue ("pid", postid); cmd.Parameters.AddWithValue ("tid", tid); cnx.Open (); cmd.ExecuteNonQuery (); } } /// /// Gets the comments. /// /// The comments. /// Postid. /// If set to true get hidden. public override Comment[] GetComments (long postid, bool getHidden) { List cmts = new List (); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "select _id, username, bcontent, modified, posted, visible from comment " + "where applicationname = :appname and postid = :id" + ((getHidden) ? " and visible = true " : " ") + "order by posted asc"; cmd.Parameters.AddWithValue ("appname", applicationName); cmd.Parameters.AddWithValue ("id", postid); cnx.Open (); using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { while (rdr.Read ()) { Comment c = new Comment (); c.CommentText = rdr.GetString (rdr.GetOrdinal ("bcontent")); c.From = rdr.GetString (rdr.GetOrdinal ("username")); c.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); c.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); c.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); c.PostId = postid; c.Id = rdr.GetInt64 (rdr.GetOrdinal ("_id")); cmts.Add (c); } } } return cmts.ToArray (); } /// /// Updates the post. /// /// Postid. /// Title. /// Content. /// If set to true visible. /// Circle identifiers public override void UpdatePost (long postid, string title, string content, bool visible, long[] cids) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { DateTime now = DateTime.Now; cmd.CommandText = "update blog set modified=:now," + " title = :title," + " bcontent = :content, " + " visible = :visible " + "where _id = :id"; cmd.Parameters.AddWithValue ("now", now); cmd.Parameters.AddWithValue ("title", title); if (content == null) content = ""; cmd.Parameters.AddWithValue ("content", content); cmd.Parameters.AddWithValue ("visible", visible); cmd.Parameters.AddWithValue ("id", postid); cnx.Open (); cmd.ExecuteNonQuery (); } cnx.Close (); } if (cids != null) UpdatePostCircles (postid, cids); } /// /// Removes the post. /// /// Postid. public override void RemovePost (long postid) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "delete from blog where _id = :id"; cmd.Parameters.AddWithValue ("id", postid); cnx.Open (); cmd.ExecuteNonQuery (); } } /// /// Comment the specified from, postid and content. /// /// From. /// Postid. /// Content. public override long Comment (string from, long postid, string content) { if (from == null) throw new ArgumentNullException ("from"); if (content == null) throw new ArgumentNullException ("content"); bool visible = AutoValidatesComments; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "insert into comment (postid,bcontent," + "modified,posted,visible,username,applicationname)" + "values (:postid,:bcontent,:modified,:posted," + ":visible,:username,:appname) returning _id"; cmd.Parameters.AddWithValue ("postid", postid); cmd.Parameters.AddWithValue ("bcontent", content); DateTime now = DateTime.Now; cmd.Parameters.AddWithValue ("modified", now); cmd.Parameters.AddWithValue ("posted", now); cmd.Parameters.AddWithValue ("visible", visible); cmd.Parameters.AddWithValue ("username", from); cmd.Parameters.AddWithValue ("appname", applicationName); cnx.Open (); return (long)cmd.ExecuteScalar (); } } /// /// Validates the comment. /// /// Cmtid. public override void ValidateComment (long cmtid) { throw new NotImplementedException (); } /// /// Updates the comment. /// /// Cmtid. /// Content. /// If set to true visible. public override void UpdateComment (long cmtid, string content, bool visible) { throw new NotImplementedException (); } private bool autoValidateComment = true; /// /// Gets or sets a value indicating whether this auto validate comment. /// /// true if auto validate comment; otherwise, false. public override bool AutoValidatesComments { get { return autoValidateComment; } set { autoValidateComment = value; } } /// /// Blogs the title. /// /// The title. /// Username. public override string BlogTitle (string username) { throw new NotImplementedException (); } #endregion /// /// Initialize the specified name and config. /// /// Name. /// Config. public override void Initialize (string name, System.Collections.Specialized.NameValueCollection config) { string cnxName = config ["connectionStringName"]; connectionString = ConfigurationManager.ConnectionStrings [cnxName].ConnectionString; config.Remove ("connectionStringName"); applicationName = config ["applicationName"]; config.Remove ("applicationName"); defaultPageSize = int.Parse (config ["pageLen"] ?? "10"); base.Initialize (name, config); } #region implemented abstract members of BlogProvider /// /// Gets the post. /// /// The post. /// Postid. public override BlogEntry GetPost (long postid) { BlogEntry be = null; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "select username, title, bcontent, modified, " + "posted, visible, photo, rate from blog " + "where applicationname = :appname and _id = :id"; cmd.Parameters.AddWithValue ("appname", applicationName); cmd.Parameters.AddWithValue ("id", postid); cnx.Open (); using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { if (rdr.Read ()) { be = new BlogEntry (); be.Title = rdr.GetString (rdr.GetOrdinal ("title")); be.Content = rdr.GetString (rdr.GetOrdinal ("bcontent")); be.Author = rdr.GetString (rdr.GetOrdinal ("username")); be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); be.Rate = rdr.GetInt32 (rdr.GetOrdinal ("rate")); int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); be.Id = postid; } } } if (be != null) Populate (be); return be; } /// /// Removes the comment. /// /// The comment. /// Cmtid. public override long RemoveComment (long cmtid) { long postid = 0; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "delete from comment where _id = :id returning postid"; cmd.Parameters.AddWithValue ("id", cmtid); cnx.Open (); postid = (long)cmd.ExecuteScalar (); } return postid; } /// /// Gets the post. /// /// The post. /// Username. /// Title. public override UUTBlogEntryCollection GetPost (string username, string title) { UUTBlogEntryCollection bec = new UUTBlogEntryCollection (username, title); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "select _id,bcontent,modified,posted,visible,photo,rate from blog " + "where applicationname = :appname and username = :username and title = :title order by rate desc"; cmd.Parameters.AddWithValue ("appname", NpgsqlDbType.Varchar, applicationName); cmd.Parameters.AddWithValue ("username", NpgsqlDbType.Varchar, username); cmd.Parameters.AddWithValue ("title", NpgsqlDbType.Varchar, title); cnx.Open (); cmd.Prepare (); using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { while (rdr.Read ()) { BlogEntry be = new BlogEntry (); be.Title = title; be.Content = rdr.GetString (rdr.GetOrdinal ("bcontent")); be.Author = username; be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); be.Rate = rdr.GetInt32 (rdr.GetOrdinal ("rate")); be.Id = rdr.GetInt64 (rdr.GetOrdinal ("_id")); { int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); } bec.Add (be); } rdr.Close (); } } if (bec.Count != 0) { Populate (bec); } } return bec; } private void SetCirclesOn (BasePost be) { List circles = new List (); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmdcircles = cnx.CreateCommand ()) { cmdcircles.CommandText = "select a.circle_id from blog_access a " + "where a.post_id = :pid"; cmdcircles.Parameters.AddWithValue ("pid", be.Id); cnx.Open (); using (NpgsqlDataReader rdr = cmdcircles.ExecuteReader ()) { while (rdr.Read ()) { circles.Add (rdr.GetInt64 (0)); } } } be.AllowedCircles = circles.ToArray (); } /// /// Removes the tag. /// /// Tagid. public override void DropTag (long tagid) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "DELETE from public.tag where _id = :tid"; cmd.Parameters.AddWithValue ("tagid", tagid); cnx.Open (); cmd.ExecuteNonQuery (); cnx.Close (); } } private static string SelectTagsSql = "SELECT tag.name, tag._id FROM public.tag WHERE name like :name"; private IEnumerable GetSuggestion (string pattern) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = SelectTagsSql; throw new NotImplementedException (); } } private static string InsertTagSql = "INSERT INTO tag (name) VALUES (:name) returning _id"; private void InsertTag (long postid, long tagid) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = InsertTagSql; throw new NotImplementedException (); } } private long GetTagId (string tagname) { if (tagname == null) throw new NullReferenceException ("This tag name is null"); long id = 0; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "SELECT tag._id FROM public.tag WHERE name = :name"; cmd.Parameters.AddWithValue ("name", tagname); cnx.Open (); id = (long)cmd.ExecuteScalar (); } return id; } private long GetOrCreateTagId (string tagname) { if (tagname == null) throw new NullReferenceException ("This tag name is null"); long id = 0; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { try { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "SELECT tag._id FROM public.tag WHERE name = :name"; cmd.Parameters.AddWithValue ("name", tagname); cnx.Open (); id = (long)cmd.ExecuteScalar (); } } catch (NullReferenceException) { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "INSERT INTO public.tag(name) VALUES (:name) RETURNING _id"; cmd.Parameters.AddWithValue ("name", tagname); id = (long)cmd.ExecuteScalar (); } } } return id; } private static string SelectPostTagsSql = "SELECT tag.name FROM public.tag, public.tagged\n" + "WHERE tag._id = tagged.tagid AND tagged.postid = :pid \n"; private void SetTagsOn (BlogEntryCollection bec) { foreach (BlogEntry be in bec) { SetTagsOn (be); } } private void SetTagsOn (BasePost be) { List tags = new List (); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmdtags = cnx.CreateCommand ()) { cmdtags.CommandText = SelectPostTagsSql; cmdtags.Parameters.AddWithValue ("pid", be.Id); cnx.Open (); using (NpgsqlDataReader rdr = cmdtags.ExecuteReader ()) { while (rdr.Read ()) { tags.Add (rdr.GetString (0)); } } } be.Tags = tags.ToArray (); } // Assert(bec!=null); private void Populate (BlogEntryCollection bec) { foreach (BlogEntry be in bec) Populate (be); } private void Populate (BasePost be) { SetTagsOn (be); SetCirclesOn (be); } /// /// Post the specified username, title, content and visible. /// /// Username. /// Title. /// Content. /// If set to true visible. /// . public override long Post (string username, string title, string content, bool visible, long[] circles) { long pid = 0; if (username == null) throw new ArgumentNullException ("username"); if (title == null) throw new ArgumentNullException ("title"); if (content == null) content = ""; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "insert into blog (title,bcontent,modified,posted,visible,username,applicationname)" + "values (:title,:bcontent,:modified,:posted,:visible,:username,:appname) returning _id"; cmd.Parameters.AddWithValue ("title", title); cmd.Parameters.AddWithValue ("bcontent", content); DateTime now = DateTime.Now; cmd.Parameters.AddWithValue ("modified", now); cmd.Parameters.AddWithValue ("posted", now); cmd.Parameters.AddWithValue ("visible", visible); cmd.Parameters.AddWithValue ("username", username); cmd.Parameters.AddWithValue ("appname", applicationName); cnx.Open (); pid = (long)cmd.ExecuteScalar (); } cnx.Close (); } if (circles != null) UpdatePostCircles (pid, circles); return pid; } /// /// Updates the post photo. /// /// Pid. /// Photo. public override void UpdatePostPhoto (long pid, string photo) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { cnx.Open (); using (NpgsqlCommand cmd = cnx.CreateCommand ()) { if (photo == null) cmd.CommandText = "update blog set photo = NULL where _id = :pid"; else { cmd.CommandText = "update blog set photo = :photo where _id = :pid"; cmd.Parameters.AddWithValue ("photo", photo); } cmd.Parameters.AddWithValue ("pid", pid); cmd.ExecuteNonQuery (); } cnx.Close (); } } private void UpdatePostCircles (long pid, long[] circles) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { cnx.Open (); using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "delete from blog_access where post_id = :pid"; cmd.Parameters.AddWithValue ("pid", pid); cmd.ExecuteNonQuery (); } if (circles != null) if (circles.Length > 0) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "insert into blog_access (post_id,circle_id) values (:pid,:cid)"; cmd.Parameters.AddWithValue ("pid", NpgsqlTypes.NpgsqlDbType.Bigint, pid); cmd.Parameters.Add ("cid", NpgsqlTypes.NpgsqlDbType.Bigint); cmd.Prepare (); foreach (long ci in circles) { cmd.Parameters ["cid"].Value = ci; cmd.ExecuteNonQuery (); } } cnx.Close (); } } /// /// Finds the post. /// /// The post. /// Reader's Name. /// Pattern. /// Searchflags. /// Page index. /// Page size. /// Total records. public override BlogEntryCollection FindPost (string readersName, string pattern, FindBlogEntryFlags searchflags, int pageIndex, int pageSize, out int totalRecords) { BlogEntryCollection c = new BlogEntryCollection (); totalRecords = 0; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { if (readersName != null) { cmd.CommandText = "select _id, title,bcontent, modified," + "posted,username,visible,photo,rate " + "from blog b left outer join " + "(select count(*)>0 acc, a.post_id pid " + "from blog_access a," + " circle_members m, users u where m.circle_id = a.circle_id " + " and m.member = u.username and u.username = :uname " + " and u.applicationname = :appname " + " group by a.post_id) ma on (ma.pid = b._id) " + "where ( ((ma.acc IS NULL or ma.acc = TRUE) and b.Visible IS TRUE ) or b.username = :uname) "; cmd.Parameters.AddWithValue ("uname", readersName); } else { cmd.CommandText = "select _id, title,bcontent,modified," + "posted,username,visible,photo,rate " + "from blog b left outer join " + "(select count(*)>0 acc, a.post_id pid " + "from blog_access a" + " group by a.post_id) ma on (ma.pid = b._id)" + " where " + " ( ma.acc IS NULL and " + " b.Visible IS TRUE and " + " applicationname = :appname ) "; } cmd.Parameters.AddWithValue ("appname", applicationName); if (pattern != null) { if ((searchflags & FindBlogEntryFlags.MatchTag) > 0) { cmd.CommandText += " AND EXISTS (SELECT tag._id FROM tag, tagged \n" + " WHERE tag._id = tagged.tagid \n" + " AND tagged.postid = b._id \n" + " AND tag.name like :tagname) \n"; cmd.Parameters.AddWithValue ("tagname", pattern); } if ((searchflags & FindBlogEntryFlags.MatchContent) > 0) { cmd.CommandText += " and bcontent like :bcontent \n"; cmd.Parameters.AddWithValue ("bcontent", pattern); } if ((searchflags & FindBlogEntryFlags.MatchTitle) > 0) { cmd.CommandText += " and title like :title"; cmd.Parameters.AddWithValue ("title", pattern); } if ((searchflags & FindBlogEntryFlags.MatchUserName) > 0) { cmd.CommandText += " and username like :username \n"; cmd.Parameters.AddWithValue ("username", pattern); } } if (((searchflags & FindBlogEntryFlags.MatchInvisible) == 0) && (readersName == null) ) { cmd.CommandText += " and visible = true \n"; } cmd.CommandText += " order by rate desc"; cnx.Open (); using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { // pageIndex became one based int firstrec = pageIndex * pageSize; int lastrec = firstrec + pageSize - 1; while (rdr.Read ()) { if (totalRecords >= firstrec && totalRecords <= lastrec) { BlogEntry be = new BlogEntry (); be.Title = rdr.GetString (rdr.GetOrdinal ("title")); be.Id = rdr.GetInt64 (rdr.GetOrdinal ("_id")); be.Content = rdr.GetString (rdr.GetOrdinal ("bcontent")); be.Author = rdr.GetString (rdr.GetOrdinal ("username")); be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); be.Rate = rdr.GetInt32 (rdr.GetOrdinal ("rate")); { int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); } c.Add (be); } totalRecords++; } rdr.Close (); } } if (c != null) Populate (c); return c; } /// /// Removes the post. /// /// Username. /// Title. public override void RemoveTitle (string username, string title) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "delete from blog where username = :username and applicationname = :appname and title=:title"; cmd.Parameters.AddWithValue ("username", username); cmd.Parameters.AddWithValue ("appname", applicationName); cmd.Parameters.AddWithValue ("title", title); cnx.Open (); cmd.ExecuteNonQuery (); cnx.Close (); } } int defaultPageSize = 10; /// /// Lasts the posts. /// /// The posts. /// Page index. /// Page size. /// Total records. public override BlogEntryCollection LastPosts (int pageIndex, int pageSize, out int totalRecords) { BlogEntryCollection c = new BlogEntryCollection (); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "select * " + "from blog where applicationname = :appname and visible = true " + " order by modified desc limit :len"; cmd.Parameters.AddWithValue ("appname", applicationName); cmd.Parameters.AddWithValue ("len", defaultPageSize * 10); cnx.Open (); using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { totalRecords = 0; int firstrec = pageIndex * pageSize; int lastrec = firstrec + pageSize - 1; while (rdr.Read ()) { if (totalRecords >= firstrec && totalRecords <= lastrec) { BlogEntry be = new BlogEntry (); be.Id = rdr.GetInt64 (rdr.GetOrdinal ("_id")); be.Title = rdr.GetString (rdr.GetOrdinal ("title")); be.Content = rdr.GetString (rdr.GetOrdinal ("bcontent")); be.Author = rdr.GetString (rdr.GetOrdinal ("username")); be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Visible = true; // because of sql code used be.Rate = rdr.GetInt32(rdr.GetOrdinal("rate")); { int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); } SetTagsOn (be); SetCirclesOn (be); c.Add (be); } totalRecords++; } } } if (c != null) Populate (c); return c; } #endregion } }