gofmt, delete works
This commit is contained in:
		
							parent
							
								
									8f9969b4cc
								
							
						
					
					
						commit
						bc7fcdde54
					
				| 
						 | 
				
			
			@ -1,35 +1,35 @@
 | 
			
		|||
package categories
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
    "database/sql"	
 | 
			
		||||
	_ "github.com/lib/pq"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	_ "github.com/lib/pq"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Category struct {
 | 
			
		||||
    Id int
 | 
			
		||||
    Name string
 | 
			
		||||
    Parent sql.NullInt64
 | 
			
		||||
    Children []*Category
 | 
			
		||||
	Id       int
 | 
			
		||||
	Name     string
 | 
			
		||||
	Parent   sql.NullInt64
 | 
			
		||||
	Children []*Category
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var CategoriesTree []*Category
 | 
			
		||||
var CategoriesFlat map[int]*Category
 | 
			
		||||
 | 
			
		||||
// Cavet: slight cheat. All parents must have Category_id < their children. 
 | 
			
		||||
// Cavet: slight cheat. All parents must have Category_id < their children.
 | 
			
		||||
func LoadCategories(db *sql.DB) {
 | 
			
		||||
    CategoriesTree = []*Category{}
 | 
			
		||||
   	CategoriesFlat = make(map[int]*Category)
 | 
			
		||||
	CategoriesTree = []*Category{}
 | 
			
		||||
	CategoriesFlat = make(map[int]*Category)
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query("select categories.id, categories.name, categories.parent_id from categories order by coalesce(parent_id, id), parent_id is not null")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("DB Error loading Categories:", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer rows.Close()
 | 
			
		||||
 | 
			
		||||
   	rows, err := db.Query("select categories.id, categories.name, categories.parent_id from categories order by coalesce(parent_id, id), parent_id is not null")
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("DB Error loading Categories:", err)
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
    defer rows.Close()
 | 
			
		||||
	
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		category := &Category{Children: []*Category{}}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,13 +39,13 @@ func LoadCategories(db *sql.DB) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		CategoriesFlat[category.Id] = category
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		if category.Parent.Valid {
 | 
			
		||||
		    pid := int(category.Parent.Int64)
 | 
			
		||||
		    CategoriesFlat[pid].Children = append(CategoriesFlat[pid].Children, category)
 | 
			
		||||
			pid := int(category.Parent.Int64)
 | 
			
		||||
			CategoriesFlat[pid].Children = append(CategoriesFlat[pid].Children, category)
 | 
			
		||||
		} else {
 | 
			
		||||
		    CategoriesTree = append(CategoriesTree, category)
 | 
			
		||||
		}    
 | 
			
		||||
			CategoriesTree = append(CategoriesTree, category)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ func Add(db *sql.DB, name string, parent int) error {
 | 
			
		|||
		_, err = db.Exec("INSERT INTO categories (name, parent_id) VALUES ($1, $2)", name, parent)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Categories DB Error Add(): " , err)
 | 
			
		||||
		fmt.Println("Categories DB Error Add(): ", err)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -67,65 +67,64 @@ func Add(db *sql.DB, name string, parent int) error {
 | 
			
		|||
//  set category of news to parent
 | 
			
		||||
//  finallly delete
 | 
			
		||||
func Delete(db *sql.DB, id int) error {
 | 
			
		||||
    rows, err := db.Query("SELECT parent_id FROM categories WHERE id=$1", id)
 | 
			
		||||
    if err != nil  {
 | 
			
		||||
        fmt.Println("Categories DB Error loading category parent id: ", err)
 | 
			
		||||
        return err
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if !rows.Next() {
 | 
			
		||||
        fmt.Println("Categories DB Error loading category parent id: no category")
 | 
			
		||||
        return errors.New("No category")
 | 
			
		||||
    } 
 | 
			
		||||
	rows, err := db.Query("SELECT parent_id FROM categories WHERE id=$1", id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Categories DB Error loading category parent id: ", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !rows.Next() {
 | 
			
		||||
		fmt.Println("Categories DB Error loading category parent id: no category")
 | 
			
		||||
		return errors.New("No category")
 | 
			
		||||
	}
 | 
			
		||||
	var parent_id sql.NullInt64
 | 
			
		||||
	err = rows.Scan(&parent_id)
 | 
			
		||||
	
 | 
			
		||||
    _, err = db.Exec("UPDATE categories SET parent_id =$2 WHERE parent_id=$1", id, parent_id)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("Categories DB error changing child parent: ", err)
 | 
			
		||||
        return err
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
   
 | 
			
		||||
    _, err = db.Exec("UPDATE news SET category_id =$2 WHERE category_id=$1", id, parent_id)
 | 
			
		||||
   if err != nil {
 | 
			
		||||
        fmt.Println("Categories DB error changing category of news: ", err)
 | 
			
		||||
        return err
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _, err = db.Exec("DELETE FROM categories WHERE id=$1", id)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("Categories DB Error Delete(): ", err)
 | 
			
		||||
    }
 | 
			
		||||
    return err
 | 
			
		||||
	_, err = db.Exec("UPDATE categories SET parent_id =$2 WHERE parent_id=$1", id, parent_id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Categories DB error changing child parent: ", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = db.Exec("UPDATE news SET category_id =$2 WHERE category_id=$1", id, parent_id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Categories DB error changing category of news: ", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = db.Exec("DELETE FROM categories WHERE id=$1", id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Categories DB Error Delete(): ", err)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (category *Category) ChangeParent(db *sql.DB, parent *Category) error {
 | 
			
		||||
    var err error 
 | 
			
		||||
    if parent == nil {
 | 
			
		||||
        _, err = db.Exec("UPDATE categories SET parent_id = NULL WHERE id = $1", category.Id)
 | 
			
		||||
    } else {
 | 
			
		||||
        _, err = db.Exec("UPDATE categories SET parent_id = $2 WHERE id = $1", category.Id, parent.Id)
 | 
			
		||||
    }
 | 
			
		||||
      
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("Categories DB Error: ", err)
 | 
			
		||||
    }
 | 
			
		||||
    return err
 | 
			
		||||
	var err error
 | 
			
		||||
	if parent == nil {
 | 
			
		||||
		_, err = db.Exec("UPDATE categories SET parent_id = NULL WHERE id = $1", category.Id)
 | 
			
		||||
	} else {
 | 
			
		||||
		_, err = db.Exec("UPDATE categories SET parent_id = $2 WHERE id = $1", category.Id, parent.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Categories DB Error: ", err)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// get the number of parents a category has
 | 
			
		||||
func (category *Category) Depth() int {
 | 
			
		||||
    depth := 0
 | 
			
		||||
    current := category
 | 
			
		||||
    for current.Parent.Valid {
 | 
			
		||||
        current = CategoriesFlat[int(current.Parent.Int64)]
 | 
			
		||||
        depth ++
 | 
			
		||||
    }
 | 
			
		||||
    return depth
 | 
			
		||||
	depth := 0
 | 
			
		||||
	current := category
 | 
			
		||||
	for current.Parent.Valid {
 | 
			
		||||
		current = CategoriesFlat[int(current.Parent.Int64)]
 | 
			
		||||
		depth++
 | 
			
		||||
	}
 | 
			
		||||
	return depth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper function for templates (add form select list)
 | 
			
		||||
func (category *Category) ToString() string {
 | 
			
		||||
    return strings.Repeat("- ", category.Depth()) + category.Name
 | 
			
		||||
}
 | 
			
		||||
	return strings.Repeat("- ", category.Depth()) + category.Name
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								main.go
								
								
								
								
							
							
						
						
									
										17
									
								
								main.go
								
								
								
								
							| 
						 | 
				
			
			@ -3,16 +3,16 @@ package main
 | 
			
		|||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/gorilla/sessions"
 | 
			
		||||
	_ "github.com/lib/pq"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"flag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	VERSION = "0.1"
 | 
			
		||||
	VERSION      = "0.1"
 | 
			
		||||
	MAX_DB_CONNS = 10 // already excessive for personal app
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ type Config struct {
 | 
			
		|||
		Password string
 | 
			
		||||
	}
 | 
			
		||||
	Port string
 | 
			
		||||
	Url string
 | 
			
		||||
	Url  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -33,9 +33,9 @@ const (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	config    Config
 | 
			
		||||
	db        *sql.DB
 | 
			
		||||
	store     = sessions.NewCookieStore([]byte("key-store-auth-secret"))
 | 
			
		||||
	config Config
 | 
			
		||||
	db     *sql.DB
 | 
			
		||||
	store  = sessions.NewCookieStore([]byte("key-store-auth-secret"))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func loadConfig(env string) {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,13 +69,12 @@ func dbConnect() {
 | 
			
		|||
 | 
			
		||||
func main() {
 | 
			
		||||
	fmt.Println("transmet ", VERSION)
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	envFlag := flag.String("env", "local", "load config/{env}.json")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	fmt.Println("Loading...")
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	loadConfig(*envFlag)
 | 
			
		||||
	dbConnect()
 | 
			
		||||
	initTemplates()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										203
									
								
								news/news.go
								
								
								
								
							
							
						
						
									
										203
									
								
								news/news.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,22 +1,21 @@
 | 
			
		|||
package news
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
    "time"
 | 
			
		||||
    "database/sql"	
 | 
			
		||||
	_ "github.com/lib/pq"
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/dballard/transmet/categories"
 | 
			
		||||
 | 
			
		||||
	_ "github.com/lib/pq"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type News struct {
 | 
			
		||||
    id int
 | 
			
		||||
    Url		string
 | 
			
		||||
    Title	string
 | 
			
		||||
    Category_id	int
 | 
			
		||||
    Date	time.Time	
 | 
			
		||||
    Notes	string
 | 
			
		||||
    Expoerted bool
 | 
			
		||||
	id          int
 | 
			
		||||
	Url         string
 | 
			
		||||
	Title       string
 | 
			
		||||
	Category_id int
 | 
			
		||||
	Date        time.Time
 | 
			
		||||
	Notes       string
 | 
			
		||||
	Expoerted   bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Storage Node containing:
 | 
			
		||||
| 
						 | 
				
			
			@ -24,55 +23,60 @@ type News struct {
 | 
			
		|||
 *  News children
 | 
			
		||||
 *  Category - category of this node
 | 
			
		||||
 *  Children - sub containers: mapped to sub categories
 | 
			
		||||
 */ 
 | 
			
		||||
 */
 | 
			
		||||
type NewsContainer struct {
 | 
			
		||||
    Name string
 | 
			
		||||
    News []News
 | 
			
		||||
    Category *categories.Category
 | 
			
		||||
    Children map[int]*NewsContainer
 | 
			
		||||
	Name     string
 | 
			
		||||
	News     []News
 | 
			
		||||
	Category *categories.Category
 | 
			
		||||
	Children map[int]*NewsContainer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Insert News item into DB
 | 
			
		||||
func (news *News) Insert(db *sql.DB) error {
 | 
			
		||||
    _, err := db.Exec("INSERT INTO news (url, title, category_id, notes) VALUES($1, $2, $3, $4)", news.Url, news.Title, news.Category_id, news.Notes );
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("Error inserting news: ", err)
 | 
			
		||||
        return err
 | 
			
		||||
    }
 | 
			
		||||
    return nil
 | 
			
		||||
	_, err := db.Exec("INSERT INTO news (url, title, category_id, notes) VALUES($1, $2, $3, $4)", news.Url, news.Title, news.Category_id, news.Notes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Error inserting news: ", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Delete(db *sql.DB, id int) error {
 | 
			
		||||
	_, err := db.Exec("DELETE FROM news WHERE id = $1", id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Error deleting news: ", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nullStringToString(str *sql.NullString) string {
 | 
			
		||||
    if str.Valid {
 | 
			
		||||
        return str.String
 | 
			
		||||
    } else {
 | 
			
		||||
        return ""
 | 
			
		||||
    }
 | 
			
		||||
	if str.Valid {
 | 
			
		||||
		return str.String
 | 
			
		||||
	} else {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init and add a news container to the Data Structs
 | 
			
		||||
func addContainer(category_id int, flat, tree map[int]*NewsContainer) {
 | 
			
		||||
    container := &NewsContainer{ Category: categories.CategoriesFlat[category_id], Name: categories.CategoriesFlat[category_id].Name, News: []News{}, Children: map[int]*NewsContainer{} }
 | 
			
		||||
    flat[category_id] = container
 | 
			
		||||
    parent := categories.CategoriesFlat[category_id].Parent
 | 
			
		||||
    if parent.Valid {
 | 
			
		||||
	container := &NewsContainer{Category: categories.CategoriesFlat[category_id], Name: categories.CategoriesFlat[category_id].Name, News: []News{}, Children: map[int]*NewsContainer{}}
 | 
			
		||||
	flat[category_id] = container
 | 
			
		||||
	parent := categories.CategoriesFlat[category_id].Parent
 | 
			
		||||
	if parent.Valid {
 | 
			
		||||
		if _, ok := flat[int(parent.Int64)]; !ok {
 | 
			
		||||
		    addContainer(int(parent.Int64), flat, tree)
 | 
			
		||||
			addContainer(int(parent.Int64), flat, tree)
 | 
			
		||||
		}
 | 
			
		||||
		flat[int(parent.Int64)].Children[category_id] = container
 | 
			
		||||
    } else {
 | 
			
		||||
        tree[category_id] = container
 | 
			
		||||
    }
 | 
			
		||||
	} else {
 | 
			
		||||
		tree[category_id] = container
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load and return in NewsContainer format all the unexported news items
 | 
			
		||||
func LoadPage(db *sql.DB, offset, amount int) ([]*News, int, error) {
 | 
			
		||||
    categories.LoadCategories(db) // required by addContainer
 | 
			
		||||
	categories.LoadCategories(db) // required by addContainer
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query("SELECT id, url, title, category_id, timestamp, notes FROM news WHERE exported is null order by timestamp DESC")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +90,7 @@ func LoadPage(db *sql.DB, offset, amount int) ([]*News, int, error) {
 | 
			
		|||
 | 
			
		||||
// Load and return in NewsContainer format all the unexported news items
 | 
			
		||||
func Unexported(db *sql.DB) (map[int]*NewsContainer, int, error) {
 | 
			
		||||
    categories.LoadCategories(db) // required by addContainer
 | 
			
		||||
	categories.LoadCategories(db) // required by addContainer
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query("SELECT id, url, title, category_id, timestamp, notes FROM news WHERE exported is null order by category_id ASC")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -94,92 +98,91 @@ func Unexported(db *sql.DB) (map[int]*NewsContainer, int, error) {
 | 
			
		|||
		return nil, 0, err
 | 
			
		||||
	}
 | 
			
		||||
	defer rows.Close()
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	return convertSqlToNewsContainer(rows)
 | 
			
		||||
}	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertSqlToNews(rows *sql.Rows) ([]*News, int, error) {
 | 
			
		||||
	news := []*News{}
 | 
			
		||||
	count := 0
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
	    newsItem := &News{}
 | 
			
		||||
	    var url, title, notes sql.NullString 
 | 
			
		||||
	    var category_id sql.NullInt64
 | 
			
		||||
	    err := rows.Scan(&newsItem.id, &url, &title, &category_id, &newsItem.Date, ¬es)
 | 
			
		||||
	    if err != nil {
 | 
			
		||||
	        fmt.Println("Error reading news from DB: " + err.Error())
 | 
			
		||||
	        return nil, 0, err
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    newsItem.Url = nullStringToString(&url)
 | 
			
		||||
	    newsItem.Title = nullStringToString(&title)
 | 
			
		||||
	    newsItem.Notes = nullStringToString(¬es)
 | 
			
		||||
	    	    
 | 
			
		||||
	    if category_id.Valid {
 | 
			
		||||
	        newsItem.Category_id = int(category_id.Int64)
 | 
			
		||||
	    } else {
 | 
			
		||||
	        continue  // needs a category id
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    
 | 
			
		||||
	    news = append(news, newsItem)
 | 
			
		||||
	    count++
 | 
			
		||||
		newsItem := &News{}
 | 
			
		||||
		var url, title, notes sql.NullString
 | 
			
		||||
		var category_id sql.NullInt64
 | 
			
		||||
		err := rows.Scan(&newsItem.id, &url, &title, &category_id, &newsItem.Date, ¬es)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("Error reading news from DB: " + err.Error())
 | 
			
		||||
			return nil, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		newsItem.Url = nullStringToString(&url)
 | 
			
		||||
		newsItem.Title = nullStringToString(&title)
 | 
			
		||||
		newsItem.Notes = nullStringToString(¬es)
 | 
			
		||||
 | 
			
		||||
		if category_id.Valid {
 | 
			
		||||
			newsItem.Category_id = int(category_id.Int64)
 | 
			
		||||
		} else {
 | 
			
		||||
			continue // needs a category id
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		news = append(news, newsItem)
 | 
			
		||||
		count++
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	return news, count, nil
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
func convertSqlToNewsContainer(rows *sql.Rows) (map[int]*NewsContainer, int, error) {
 | 
			
		||||
	newsTree := map[int]*NewsContainer{}
 | 
			
		||||
	newsFlat := map[int]*NewsContainer{}
 | 
			
		||||
	count := 0
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
	    news := News{}
 | 
			
		||||
	    var url, title, notes sql.NullString 
 | 
			
		||||
	    var category_id sql.NullInt64
 | 
			
		||||
	    err := rows.Scan(&news.id, &url, &title, &category_id, &news.Date, ¬es)
 | 
			
		||||
	    if err != nil {
 | 
			
		||||
	        fmt.Println("Error reading news from DB: " + err.Error())
 | 
			
		||||
	        return nil, 0, err
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    news.Url = nullStringToString(&url)
 | 
			
		||||
	    news.Title = nullStringToString(&title)
 | 
			
		||||
	    news.Notes = nullStringToString(¬es)
 | 
			
		||||
	    
 | 
			
		||||
	    if category_id.Valid {
 | 
			
		||||
	        news.Category_id = int(category_id.Int64)
 | 
			
		||||
	    } else {
 | 
			
		||||
	        continue  // needs a category id
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    if _, ok := newsFlat[news.Category_id]; !ok {
 | 
			
		||||
	       addContainer(news.Category_id, newsFlat, newsTree)
 | 
			
		||||
	    } 
 | 
			
		||||
	    container := newsFlat[news.Category_id]
 | 
			
		||||
	    container.News = append(container.News, news)
 | 
			
		||||
	    count++
 | 
			
		||||
		news := News{}
 | 
			
		||||
		var url, title, notes sql.NullString
 | 
			
		||||
		var category_id sql.NullInt64
 | 
			
		||||
		err := rows.Scan(&news.id, &url, &title, &category_id, &news.Date, ¬es)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("Error reading news from DB: " + err.Error())
 | 
			
		||||
			return nil, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		news.Url = nullStringToString(&url)
 | 
			
		||||
		news.Title = nullStringToString(&title)
 | 
			
		||||
		news.Notes = nullStringToString(¬es)
 | 
			
		||||
 | 
			
		||||
		if category_id.Valid {
 | 
			
		||||
			news.Category_id = int(category_id.Int64)
 | 
			
		||||
		} else {
 | 
			
		||||
			continue // needs a category id
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, ok := newsFlat[news.Category_id]; !ok {
 | 
			
		||||
			addContainer(news.Category_id, newsFlat, newsTree)
 | 
			
		||||
		}
 | 
			
		||||
		container := newsFlat[news.Category_id]
 | 
			
		||||
		container.News = append(container.News, news)
 | 
			
		||||
		count++
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	return newsTree, count, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper fn - formating - math 
 | 
			
		||||
// Helper fn - formating - math
 | 
			
		||||
func (this *NewsContainer) HeaderDepth(start int) int {
 | 
			
		||||
    return start + this.Category.Depth()
 | 
			
		||||
	return start + this.Category.Depth()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mark the current batch (news.exported is null) as exported in this batch (exported = now())
 | 
			
		||||
func MarkExported(db *sql.DB) error {
 | 
			
		||||
    now := time.Now()
 | 
			
		||||
    
 | 
			
		||||
    _, err := db.Exec("UPDATE news SET exported=$1 WHERE exported is null", now)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("DB errror: news.MarkExported():", err)
 | 
			
		||||
    }
 | 
			
		||||
    return err
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
 | 
			
		||||
	_, err := db.Exec("UPDATE news SET exported=$1 WHERE exported is null", now)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("DB errror: news.MarkExported():", err)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (news *News) Id() int {
 | 
			
		||||
    return news.id
 | 
			
		||||
}
 | 
			
		||||
	return news.id
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +1,21 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/gorilla/sessions"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"github.com/dballard/transmet/user"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"github.com/dballard/transmet/categories"
 | 
			
		||||
	"github.com/dballard/transmet/news"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	txtTemplate "text/template"
 | 
			
		||||
	"github.com/dballard/transmet/user"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/gorilla/sessions"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	txtTemplate "text/template"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func GetFlashes(session *sessions.Session) map[string]interface{} {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ func LoginFormHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	session, _ := store.Get(r, "c_user")
 | 
			
		||||
	flashes := GetFlashes(session)
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	ShowTemplate("login", w, map[string]interface{}{"flashes": flashes})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ func LoginPostHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
		session.Values["username"] = user.Username
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
		if r.URL.Query().Get("url") != "" {
 | 
			
		||||
		    http.Redirect(w, r, "/add?" + r.URL.RawQuery, http.StatusFound)
 | 
			
		||||
			http.Redirect(w, r, "/add?"+r.URL.RawQuery, http.StatusFound)
 | 
			
		||||
		}
 | 
			
		||||
		http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -107,31 +107,31 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request, user *user.User, sess
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func getUrlTitle(url string) string {
 | 
			
		||||
    resp, err := http.Get(url)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("Error looking up link", url, ":", err)
 | 
			
		||||
    } else {
 | 
			
		||||
        body, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
        if err != nil {
 | 
			
		||||
            fmt.Println("Error reading link", url, ":", err)
 | 
			
		||||
        } else {
 | 
			
		||||
            re := regexp.MustCompile("< *[Tt][Ii][Tt][Ll][Ee] *>(.*)</ *[Tt][Ii][Tt][Ll][Ee] *>")
 | 
			
		||||
            title := re.FindStringSubmatch(string(body))
 | 
			
		||||
            if title != nil {
 | 
			
		||||
                return strings.TrimSpace(title[1])
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return ""
 | 
			
		||||
	resp, err := http.Get(url)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Error looking up link", url, ":", err)
 | 
			
		||||
	} else {
 | 
			
		||||
		body, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("Error reading link", url, ":", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			re := regexp.MustCompile("< *[Tt][Ii][Tt][Ll][Ee] *>(.*)</ *[Tt][Ii][Tt][Ll][Ee] *>")
 | 
			
		||||
			title := re.FindStringSubmatch(string(body))
 | 
			
		||||
			if title != nil {
 | 
			
		||||
				return strings.TrimSpace(title[1])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ?url=
 | 
			
		||||
func addFormHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
    categories.LoadCategories(db)
 | 
			
		||||
    
 | 
			
		||||
    flashes := GetFlashes(session)
 | 
			
		||||
    popup := session.Values["popup"]
 | 
			
		||||
    delete(session.Values, "popup")
 | 
			
		||||
	categories.LoadCategories(db)
 | 
			
		||||
 | 
			
		||||
	flashes := GetFlashes(session)
 | 
			
		||||
	popup := session.Values["popup"]
 | 
			
		||||
	delete(session.Values, "popup")
 | 
			
		||||
	title := session.Values["title"]
 | 
			
		||||
	delete(session.Values, "title")
 | 
			
		||||
	link := session.Values["link"]
 | 
			
		||||
| 
						 | 
				
			
			@ -139,239 +139,238 @@ func addFormHandler(w http.ResponseWriter, r *http.Request, user *user.User, ses
 | 
			
		|||
	description := session.Values["description"]
 | 
			
		||||
	delete(session.Values, "description")
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if link != nil {
 | 
			
		||||
	    //TODO category_id
 | 
			
		||||
		//TODO category_id
 | 
			
		||||
		ShowTemplate("add", w, map[string]interface{}{"user": user, "flashes": flashes, "categories": categories.CategoriesTree, "link": link, "title": title, "description": description, "popup": popup})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
    
 | 
			
		||||
    var url = r.URL.Query().Get("url")
 | 
			
		||||
    reHttp := regexp.MustCompile("^https?://")
 | 
			
		||||
    if url != "" && ! reHttp.Match([]byte(url)) {
 | 
			
		||||
        url = "http://" + url
 | 
			
		||||
    } 
 | 
			
		||||
    
 | 
			
		||||
    title = r.URL.Query().Get("title")
 | 
			
		||||
    if title == "" && url != "" {
 | 
			
		||||
        title = getUrlTitle(url)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    popup = r.URL.Query().Get("popup")
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
	var url = r.URL.Query().Get("url")
 | 
			
		||||
	reHttp := regexp.MustCompile("^https?://")
 | 
			
		||||
	if url != "" && !reHttp.Match([]byte(url)) {
 | 
			
		||||
		url = "http://" + url
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	title = r.URL.Query().Get("title")
 | 
			
		||||
	if title == "" && url != "" {
 | 
			
		||||
		title = getUrlTitle(url)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	popup = r.URL.Query().Get("popup")
 | 
			
		||||
 | 
			
		||||
	ShowTemplate("add", w, map[string]interface{}{"user": user, "flashes": flashes, "link": url, "categories": categories.CategoriesTree, "title": title, "popup": popup})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addPostHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
	var news news.News
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	news.Title = r.FormValue("title")
 | 
			
		||||
	news.Notes = r.FormValue("notes")
 | 
			
		||||
	news.Url = r.FormValue("link")
 | 
			
		||||
	popup := r.FormValue("popup")
 | 
			
		||||
	category_id, err := strconv.Atoi(r.FormValue("category"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    category_id = -1
 | 
			
		||||
		category_id = -1
 | 
			
		||||
	}
 | 
			
		||||
	news.Category_id = category_id
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	err = (&news).Insert(db)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    session.AddFlash("Error saving news: " + err.Error(), flash_err)
 | 
			
		||||
	    session.Values["title"] = news.Title
 | 
			
		||||
	    session.Values["link"] = news.Url
 | 
			
		||||
	    session.Values["notes"] = news.Notes
 | 
			
		||||
	    session.Values["popup"] = popup
 | 
			
		||||
		session.AddFlash("Error saving news: "+err.Error(), flash_err)
 | 
			
		||||
		session.Values["title"] = news.Title
 | 
			
		||||
		session.Values["link"] = news.Url
 | 
			
		||||
		session.Values["notes"] = news.Notes
 | 
			
		||||
		session.Values["popup"] = popup
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	    http.Redirect(w, r, "/add", http.StatusFound)
 | 
			
		||||
		http.Redirect(w, r, "/add", http.StatusFound)
 | 
			
		||||
	} else {
 | 
			
		||||
	    // TODO auto close? redirect
 | 
			
		||||
	    session.AddFlash("Added news \""+news.Title+"\"", flash_info)
 | 
			
		||||
		// TODO auto close? redirect
 | 
			
		||||
		session.AddFlash("Added news \""+news.Title+"\"", flash_info)
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
		if popup == "1" {
 | 
			
		||||
		    http.Redirect(w, r, "/added", http.StatusFound)
 | 
			
		||||
			http.Redirect(w, r, "/added", http.StatusFound)
 | 
			
		||||
		} else {
 | 
			
		||||
	    	http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
	    }
 | 
			
		||||
			http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func templateFormHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
    flashes := GetFlashes(session)
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    
 | 
			
		||||
    news, count, err := news.Unexported(db)
 | 
			
		||||
	flashes := GetFlashes(session)
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
 | 
			
		||||
	news, count, err := news.Unexported(db)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    return
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	var templateBuf bytes.Buffer
 | 
			
		||||
	template, err := txtTemplate.ParseFiles("templates/html_template.txt")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    fmt.Println("Error processing html_tempalte:" , err)
 | 
			
		||||
		fmt.Println("Error processing html_tempalte:", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = template.Execute(&templateBuf, map[string]interface{}{"news": news})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Exec err: ", err)
 | 
			
		||||
	}
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
	ShowTemplate("export", w, map[string]interface{}{"user": user, "flashes": flashes, "template": &templateBuf, "count": count, "url": config.Url})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func exportHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
	err := news.MarkExported(db)
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    session.AddFlash("Error marking last batch of news exported", flash_err)
 | 
			
		||||
		session.AddFlash("Error marking last batch of news exported", flash_err)
 | 
			
		||||
	} else {
 | 
			
		||||
		session.AddFlash("Last batch of news marked exported", flash_info)
 | 
			
		||||
	}
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
    http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
	http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addedHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
    flashes := GetFlashes(session)
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    ShowTemplate("added", w, map[string]interface{}{"user": user, "flashes": flashes})
 | 
			
		||||
	flashes := GetFlashes(session)
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	ShowTemplate("added", w, map[string]interface{}{"user": user, "flashes": flashes})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
	id, idErr := strconv.Atoi(r.FormValue("id"))
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if idErr != nil {
 | 
			
		||||
		session.AddFlash("Invalid news to delete", flash_err)	
 | 
			
		||||
		session.AddFlash("Invalid news to delete", flash_err)
 | 
			
		||||
	} else {
 | 
			
		||||
	    err := news.Delete(db, id)
 | 
			
		||||
	    if err != nil {
 | 
			
		||||
	        session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
	    } else {
 | 
			
		||||
	    	session.AddFlash("Deleted news post", flash_info)
 | 
			
		||||
	    }	
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
		err := news.Delete(db, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
		} else {
 | 
			
		||||
			session.AddFlash("Deleted news post", flash_info)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	http.Redirect(w, r, "/", http.StatusFound)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func categoriesFormHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
    flashes := GetFlashes(session)
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    categories.LoadCategories(db)
 | 
			
		||||
	flashes := GetFlashes(session)
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	categories.LoadCategories(db)
 | 
			
		||||
 | 
			
		||||
    ShowTemplate("categories", w, map[string]interface{}{"user": user, "flashes": flashes, "categories": categories.CategoriesTree})
 | 
			
		||||
	ShowTemplate("categories", w, map[string]interface{}{"user": user, "flashes": flashes, "categories": categories.CategoriesTree})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func categoriesPostHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
    http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
	http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func categoryFromReqArg(arg string) *categories.Category {
 | 
			
		||||
    if cid, err := strconv.Atoi(arg); err != nil {
 | 
			
		||||
        return nil
 | 
			
		||||
    } else if category, ok := categories.CategoriesFlat[cid]; !ok {
 | 
			
		||||
        return nil
 | 
			
		||||
    } else {
 | 
			
		||||
        return category
 | 
			
		||||
    }
 | 
			
		||||
	if cid, err := strconv.Atoi(arg); err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else if category, ok := categories.CategoriesFlat[cid]; !ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return category
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func categoryChangeParentHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
	categories.LoadCategories(db)
 | 
			
		||||
	category := categoryFromReqArg(r.FormValue("cid"))
 | 
			
		||||
	parent := categoryFromReqArg(r.FormValue("parent"))
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if category == nil {
 | 
			
		||||
		session.AddFlash("Invalid category", flash_err)	
 | 
			
		||||
	} else if parent != nil && category.Id == parent.Id { 
 | 
			
		||||
		session.AddFlash("Invalid category", flash_err)
 | 
			
		||||
	} else if parent != nil && category.Id == parent.Id {
 | 
			
		||||
		session.AddFlash("Cannot set category parent to itself", flash_err)
 | 
			
		||||
	} else {
 | 
			
		||||
	    err := category.ChangeParent(db, parent)
 | 
			
		||||
	    if err != nil {
 | 
			
		||||
	        session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
	    } else {
 | 
			
		||||
	    	session.AddFlash("Changed category parent", flash_info)
 | 
			
		||||
	    }	
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
		err := category.ChangeParent(db, parent)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
		} else {
 | 
			
		||||
			session.AddFlash("Changed category parent", flash_info)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func categoryAddHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
	name := r.FormValue("name")
 | 
			
		||||
	parent, perr := strconv.Atoi(r.FormValue("parent"))
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if perr != nil {
 | 
			
		||||
	    parent = -1
 | 
			
		||||
		parent = -1
 | 
			
		||||
	}
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		session.AddFlash("Invalid category name", flash_err)	
 | 
			
		||||
		session.AddFlash("Invalid category name", flash_err)
 | 
			
		||||
	} else {
 | 
			
		||||
	    err := categories.Add(db, name, parent)
 | 
			
		||||
	    if err != nil {
 | 
			
		||||
	        session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
	    } else {
 | 
			
		||||
	    	session.AddFlash("Added category", flash_info)
 | 
			
		||||
	    }	
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
		err := categories.Add(db, name, parent)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
		} else {
 | 
			
		||||
			session.AddFlash("Added category", flash_info)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func categoryDeleteHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
	id, idErr := strconv.Atoi(r.FormValue("id"))
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if idErr != nil {
 | 
			
		||||
		session.AddFlash("Invalid category to delete", flash_err)	
 | 
			
		||||
		session.AddFlash("Invalid category to delete", flash_err)
 | 
			
		||||
	} else {
 | 
			
		||||
	    err := categories.Delete(db, id)
 | 
			
		||||
	    if err != nil {
 | 
			
		||||
	        session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
	    } else {
 | 
			
		||||
	    	session.AddFlash("Deleted category", flash_info)
 | 
			
		||||
	    }	
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
		err := categories.Delete(db, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			session.AddFlash("Error commiting to Database", flash_err)
 | 
			
		||||
		} else {
 | 
			
		||||
			session.AddFlash("Deleted category", flash_info)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	http.Redirect(w, r, "/categories", http.StatusFound)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newsFormHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
 | 
			
		||||
    flashes := GetFlashes(session)
 | 
			
		||||
    session.Save(r, w)
 | 
			
		||||
    var offset = 0
 | 
			
		||||
    var amount = 100
 | 
			
		||||
    
 | 
			
		||||
    argOffset, eOffset := strconv.Atoi(r.FormValue("offset"))
 | 
			
		||||
    if eOffset == nil {
 | 
			
		||||
        offset = amount * argOffset 
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    news, count, err := news.LoadPage(db, offset, amount)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    session.AddFlash("Error loading news", flash_err)
 | 
			
		||||
	flashes := GetFlashes(session)
 | 
			
		||||
	session.Save(r, w)
 | 
			
		||||
	var offset = 0
 | 
			
		||||
	var amount = 100
 | 
			
		||||
 | 
			
		||||
	argOffset, eOffset := strconv.Atoi(r.FormValue("offset"))
 | 
			
		||||
	if eOffset == nil {
 | 
			
		||||
		offset = amount * argOffset
 | 
			
		||||
	}
 | 
			
		||||
    
 | 
			
		||||
    ShowTemplate("news", w, map[string]interface{}{"user": user, "flashes": flashes, "news": news, "count": count, "categories": categories.CategoriesFlat})
 | 
			
		||||
   	
 | 
			
		||||
 | 
			
		||||
	news, count, err := news.LoadPage(db, offset, amount)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		session.AddFlash("Error loading news", flash_err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShowTemplate("news", w, map[string]interface{}{"user": user, "flashes": flashes, "news": news, "count": count, "categories": categories.CategoriesFlat})
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ServeFileHandler(res http.ResponseWriter, req *http.Request) {  
 | 
			
		||||
      fname := path.Base(req.URL.Path)  
 | 
			
		||||
      http.ServeFile(res, req, "./"+fname)  
 | 
			
		||||
 }  
 | 
			
		||||
func ServeFileHandler(res http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	fname := path.Base(req.URL.Path)
 | 
			
		||||
	http.ServeFile(res, req, "./"+fname)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init_route_handlers() {
 | 
			
		||||
    http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("js/"))))
 | 
			
		||||
	http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("js/"))))
 | 
			
		||||
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css/"))))
 | 
			
		||||
	http.Handle("/fonts/", http.StripPrefix("/fonts", http.FileServer(http.Dir("fonts/"))))
 | 
			
		||||
	http.HandleFunc("/favicon.ico", ServeFileHandler)
 | 
			
		||||
 | 
			
		||||
	r := mux.NewRouter()
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	r.HandleFunc("/login", getPostHandler(LoginFormHandler, LoginPostHandler))
 | 
			
		||||
	r.HandleFunc("/logout", userHandler(LogoutHandler))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -382,11 +381,11 @@ func init_route_handlers() {
 | 
			
		|||
	r.HandleFunc("/export-commit", userHandler(exportHandler))
 | 
			
		||||
	r.HandleFunc("/added", userHandler(addedHandler))
 | 
			
		||||
	r.HandleFunc("/delete", userHandler(deleteHandler))
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	r.HandleFunc("/categories", getPostHandler(userHandler(categoriesFormHandler), userHandler(categoriesPostHandler)))
 | 
			
		||||
	r.HandleFunc("/categories/change-parent", userHandler(categoryChangeParentHandler))
 | 
			
		||||
	r.HandleFunc("/categories/add", userHandler(categoryAddHandler))
 | 
			
		||||
	r.HandleFunc("/categories/delete", userHandler(categoryDeleteHandler))
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	http.Handle("/", r)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										87
									
								
								templates.go
								
								
								
								
							
							
						
						
									
										87
									
								
								templates.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,81 +1,80 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
    "fmt"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/dballard/transmet/categories"
 | 
			
		||||
	"html/template"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"time"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"github.com/dballard/transmet/categories"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
    	templates = map[string]*template.Template{}
 | 
			
		||||
	templates = map[string]*template.Template{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// template helper function
 | 
			
		||||
func dict (values ...interface{}) (map[string]interface{}, error) {
 | 
			
		||||
func dict(values ...interface{}) (map[string]interface{}, error) {
 | 
			
		||||
	if len(values)%2 != 0 {
 | 
			
		||||
        return nil, errors.New("invalid dict call")
 | 
			
		||||
    }
 | 
			
		||||
    dict := make(map[string]interface{}, len(values)/2)
 | 
			
		||||
    for i := 0; i < len(values); i+=2 {
 | 
			
		||||
        key, ok := values[i].(string)
 | 
			
		||||
       if !ok {
 | 
			
		||||
           return nil, errors.New("dict keys must be strings")
 | 
			
		||||
       }
 | 
			
		||||
        dict[key] = values[i+1]
 | 
			
		||||
    }
 | 
			
		||||
    return dict, nil
 | 
			
		||||
		return nil, errors.New("invalid dict call")
 | 
			
		||||
	}
 | 
			
		||||
	dict := make(map[string]interface{}, len(values)/2)
 | 
			
		||||
	for i := 0; i < len(values); i += 2 {
 | 
			
		||||
		key, ok := values[i].(string)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, errors.New("dict keys must be strings")
 | 
			
		||||
		}
 | 
			
		||||
		dict[key] = values[i+1]
 | 
			
		||||
	}
 | 
			
		||||
	return dict, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// string multiplication
 | 
			
		||||
// stringTimes(3, "Foo") => "FooFooFoo"
 | 
			
		||||
func stringTimes(times int, str string) string {
 | 
			
		||||
    result := ""
 | 
			
		||||
    for i := 0; i < times; i ++ {
 | 
			
		||||
        result += str
 | 
			
		||||
    }
 | 
			
		||||
    return result
 | 
			
		||||
} 
 | 
			
		||||
	result := ""
 | 
			
		||||
	for i := 0; i < times; i++ {
 | 
			
		||||
		result += str
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Turns a Time into a formated string
 | 
			
		||||
func dateFormat(t time.Time) string {
 | 
			
		||||
    return t.Format("2006.01.02 15:04:05")
 | 
			
		||||
	return t.Format("2006.01.02 15:04:05")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// takes a category_id and returns "Root / Parent / Category"
 | 
			
		||||
func fullCategoryPath(categoriesFlat map[int]*categories.Category, category_id int) string {
 | 
			
		||||
    var categoryNames []string = nil
 | 
			
		||||
    for category := categoriesFlat[category_id]; category != nil; category = categoriesFlat[int(category.Parent.Int64)] {
 | 
			
		||||
        categoryNames = append([]string{category.Name}, categoryNames...)
 | 
			
		||||
    }
 | 
			
		||||
    return strings.Join(categoryNames, " / ")
 | 
			
		||||
	var categoryNames []string = nil
 | 
			
		||||
	for category := categoriesFlat[category_id]; category != nil; category = categoriesFlat[int(category.Parent.Int64)] {
 | 
			
		||||
		categoryNames = append([]string{category.Name}, categoryNames...)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(categoryNames, " / ")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// truncate a string
 | 
			
		||||
func truncate(str string, maxLen int) string {
 | 
			
		||||
    if len(str) <= maxLen {
 | 
			
		||||
        return str
 | 
			
		||||
    }
 | 
			
		||||
    return str[0:maxLen] + "..."
 | 
			
		||||
	if len(str) <= maxLen {
 | 
			
		||||
		return str
 | 
			
		||||
	}
 | 
			
		||||
	return str[0:maxLen] + "..."
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tempalte helper functions
 | 
			
		||||
var funcMap = template.FuncMap {
 | 
			
		||||
    "add": func (x, y int) int { return x + y },
 | 
			
		||||
    "minus": func (x, y int) int { return x - y },
 | 
			
		||||
    "dict": dict,
 | 
			
		||||
    "stringTimes": stringTimes,
 | 
			
		||||
    "dateFormat": dateFormat,
 | 
			
		||||
    "fullCategoryPath": fullCategoryPath,
 | 
			
		||||
    "truncate": truncate,
 | 
			
		||||
var funcMap = template.FuncMap{
 | 
			
		||||
	"add":              func(x, y int) int { return x + y },
 | 
			
		||||
	"minus":            func(x, y int) int { return x - y },
 | 
			
		||||
	"dict":             dict,
 | 
			
		||||
	"stringTimes":      stringTimes,
 | 
			
		||||
	"dateFormat":       dateFormat,
 | 
			
		||||
	"fullCategoryPath": fullCategoryPath,
 | 
			
		||||
	"truncate":         truncate,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func initTemplates() {
 | 
			
		||||
	files, _ := filepath.Glob("templates/pages/*.html")
 | 
			
		||||
	re := regexp.MustCompile("templates/pages/(.*).html")
 | 
			
		||||
| 
						 | 
				
			
			@ -93,4 +92,4 @@ func ShowTemplate(template string, w http.ResponseWriter, data map[string]interf
 | 
			
		|||
		fmt.Println("Exec err: ", err)
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: show error 500 page
 | 
			
		||||
}	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,8 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
	Username    string
 | 
			
		||||
	db          *sql.DB
 | 
			
		||||
	Username string
 | 
			
		||||
	db       *sql.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func UsernameExists(db *sql.DB, username string) (bool, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ func NewUserFromAuth(db *sql.DB, username, password string) *User {
 | 
			
		|||
			fmt.Println("scan err: ", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
	    fmt.Println("no match")
 | 
			
		||||
		fmt.Println("no match")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return &user
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue