Fossil SCM
Merged in [timeline-rss-ticket] changes.
Commit
dbaf5209105ee8346acc6742ae48893ec133eb95
Parent
12ff5ff85e0b61f…
5 files changed
+21
+21
+10
-2
+10
-2
+56
M
src/db.c
+21
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1872,10 +1872,31 @@ | ||
| 1872 | 1872 | return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); |
| 1873 | 1873 | } |
| 1874 | 1874 | void db_lset_int(const char *zName, int value){ |
| 1875 | 1875 | db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); |
| 1876 | 1876 | } |
| 1877 | + | |
| 1878 | +/* | |
| 1879 | +** Returns non-0 if the database (which must be open) table identified | |
| 1880 | +** by zTableName has a column named zColName (case-sensitive), else | |
| 1881 | +** returns 0. | |
| 1882 | +*/ | |
| 1883 | +int db_table_has_column( char const *zTableName, char const *zColName ){ | |
| 1884 | + Stmt q = empty_Stmt; | |
| 1885 | + int rc = 0; | |
| 1886 | + db_prepare( &q, "PRAGMA table_info(%Q)", zTableName ); | |
| 1887 | + while(SQLITE_ROW == db_step(&q)){ | |
| 1888 | + /* Columns: (cid, name, type, notnull, dflt_value, pk) */ | |
| 1889 | + char const * zCol = db_column_text(&q, 1); | |
| 1890 | + if(0==fossil_strcmp(zColName, zCol)){ | |
| 1891 | + rc = 1; | |
| 1892 | + break; | |
| 1893 | + } | |
| 1894 | + } | |
| 1895 | + db_finalize(&q); | |
| 1896 | + return rc; | |
| 1897 | +} | |
| 1877 | 1898 | |
| 1878 | 1899 | /* |
| 1879 | 1900 | ** Record the name of a local repository in the global_config() database. |
| 1880 | 1901 | ** The repository filename %s is recorded as an entry with a "name" field |
| 1881 | 1902 | ** of the following form: |
| 1882 | 1903 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1872,10 +1872,31 @@ | |
| 1872 | return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); |
| 1873 | } |
| 1874 | void db_lset_int(const char *zName, int value){ |
| 1875 | db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); |
| 1876 | } |
| 1877 | |
| 1878 | /* |
| 1879 | ** Record the name of a local repository in the global_config() database. |
| 1880 | ** The repository filename %s is recorded as an entry with a "name" field |
| 1881 | ** of the following form: |
| 1882 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1872,10 +1872,31 @@ | |
| 1872 | return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); |
| 1873 | } |
| 1874 | void db_lset_int(const char *zName, int value){ |
| 1875 | db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); |
| 1876 | } |
| 1877 | |
| 1878 | /* |
| 1879 | ** Returns non-0 if the database (which must be open) table identified |
| 1880 | ** by zTableName has a column named zColName (case-sensitive), else |
| 1881 | ** returns 0. |
| 1882 | */ |
| 1883 | int db_table_has_column( char const *zTableName, char const *zColName ){ |
| 1884 | Stmt q = empty_Stmt; |
| 1885 | int rc = 0; |
| 1886 | db_prepare( &q, "PRAGMA table_info(%Q)", zTableName ); |
| 1887 | while(SQLITE_ROW == db_step(&q)){ |
| 1888 | /* Columns: (cid, name, type, notnull, dflt_value, pk) */ |
| 1889 | char const * zCol = db_column_text(&q, 1); |
| 1890 | if(0==fossil_strcmp(zColName, zCol)){ |
| 1891 | rc = 1; |
| 1892 | break; |
| 1893 | } |
| 1894 | } |
| 1895 | db_finalize(&q); |
| 1896 | return rc; |
| 1897 | } |
| 1898 | |
| 1899 | /* |
| 1900 | ** Record the name of a local repository in the global_config() database. |
| 1901 | ** The repository filename %s is recorded as an entry with a "name" field |
| 1902 | ** of the following form: |
| 1903 |
M
src/db.c
+21
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1872,10 +1872,31 @@ | ||
| 1872 | 1872 | return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); |
| 1873 | 1873 | } |
| 1874 | 1874 | void db_lset_int(const char *zName, int value){ |
| 1875 | 1875 | db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); |
| 1876 | 1876 | } |
| 1877 | + | |
| 1878 | +/* | |
| 1879 | +** Returns non-0 if the database (which must be open) table identified | |
| 1880 | +** by zTableName has a column named zColName (case-sensitive), else | |
| 1881 | +** returns 0. | |
| 1882 | +*/ | |
| 1883 | +int db_table_has_column( char const *zTableName, char const *zColName ){ | |
| 1884 | + Stmt q = empty_Stmt; | |
| 1885 | + int rc = 0; | |
| 1886 | + db_prepare( &q, "PRAGMA table_info(%Q)", zTableName ); | |
| 1887 | + while(SQLITE_ROW == db_step(&q)){ | |
| 1888 | + /* Columns: (cid, name, type, notnull, dflt_value, pk) */ | |
| 1889 | + char const * zCol = db_column_text(&q, 1); | |
| 1890 | + if(0==fossil_strcmp(zColName, zCol)){ | |
| 1891 | + rc = 1; | |
| 1892 | + break; | |
| 1893 | + } | |
| 1894 | + } | |
| 1895 | + db_finalize(&q); | |
| 1896 | + return rc; | |
| 1897 | +} | |
| 1877 | 1898 | |
| 1878 | 1899 | /* |
| 1879 | 1900 | ** Record the name of a local repository in the global_config() database. |
| 1880 | 1901 | ** The repository filename %s is recorded as an entry with a "name" field |
| 1881 | 1902 | ** of the following form: |
| 1882 | 1903 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1872,10 +1872,31 @@ | |
| 1872 | return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); |
| 1873 | } |
| 1874 | void db_lset_int(const char *zName, int value){ |
| 1875 | db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); |
| 1876 | } |
| 1877 | |
| 1878 | /* |
| 1879 | ** Record the name of a local repository in the global_config() database. |
| 1880 | ** The repository filename %s is recorded as an entry with a "name" field |
| 1881 | ** of the following form: |
| 1882 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1872,10 +1872,31 @@ | |
| 1872 | return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); |
| 1873 | } |
| 1874 | void db_lset_int(const char *zName, int value){ |
| 1875 | db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); |
| 1876 | } |
| 1877 | |
| 1878 | /* |
| 1879 | ** Returns non-0 if the database (which must be open) table identified |
| 1880 | ** by zTableName has a column named zColName (case-sensitive), else |
| 1881 | ** returns 0. |
| 1882 | */ |
| 1883 | int db_table_has_column( char const *zTableName, char const *zColName ){ |
| 1884 | Stmt q = empty_Stmt; |
| 1885 | int rc = 0; |
| 1886 | db_prepare( &q, "PRAGMA table_info(%Q)", zTableName ); |
| 1887 | while(SQLITE_ROW == db_step(&q)){ |
| 1888 | /* Columns: (cid, name, type, notnull, dflt_value, pk) */ |
| 1889 | char const * zCol = db_column_text(&q, 1); |
| 1890 | if(0==fossil_strcmp(zColName, zCol)){ |
| 1891 | rc = 1; |
| 1892 | break; |
| 1893 | } |
| 1894 | } |
| 1895 | db_finalize(&q); |
| 1896 | return rc; |
| 1897 | } |
| 1898 | |
| 1899 | /* |
| 1900 | ** Record the name of a local repository in the global_config() database. |
| 1901 | ** The repository filename %s is recorded as an entry with a "name" field |
| 1902 | ** of the following form: |
| 1903 |
+10
-2
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1665,11 +1665,11 @@ | ||
| 1665 | 1665 | const char *zUuid; |
| 1666 | 1666 | char zTktName[UUID_SIZE+1]; |
| 1667 | 1667 | Manifest *pTktChng; |
| 1668 | 1668 | int modPending; |
| 1669 | 1669 | const char *zModAction; |
| 1670 | - | |
| 1670 | + char *zTktTitle; | |
| 1671 | 1671 | login_check_credentials(); |
| 1672 | 1672 | if( !g.perm.RdTkt ){ login_needed(); return; } |
| 1673 | 1673 | rid = name_to_rid_www("name"); |
| 1674 | 1674 | if( rid==0 ){ fossil_redirect_home(); } |
| 1675 | 1675 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1694,10 +1694,13 @@ | ||
| 1694 | 1694 | } |
| 1695 | 1695 | if( strcmp(zModAction,"approve")==0 ){ |
| 1696 | 1696 | moderation_approve(rid); |
| 1697 | 1697 | } |
| 1698 | 1698 | } |
| 1699 | + zTktTitle = db_table_has_column( "ticket", "title" ) | |
| 1700 | + ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName) | |
| 1701 | + : 0; | |
| 1699 | 1702 | style_header("Ticket Change Details"); |
| 1700 | 1703 | style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid); |
| 1701 | 1704 | style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); |
| 1702 | 1705 | style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); |
| 1703 | 1706 | style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); |
| @@ -1718,17 +1721,22 @@ | ||
| 1718 | 1721 | modPending = moderation_pending(rid); |
| 1719 | 1722 | if( modPending ){ |
| 1720 | 1723 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 1721 | 1724 | } |
| 1722 | 1725 | @ <tr><th>Ticket:</th> |
| 1723 | - @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr> | |
| 1726 | + @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> | |
| 1727 | + if(zTktTitle){ | |
| 1728 | + @<br>%h(zTktTitle) | |
| 1729 | + } | |
| 1730 | + @</td></tr> | |
| 1724 | 1731 | @ <tr><th>Date:</th><td> |
| 1725 | 1732 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1726 | 1733 | @ <tr><th>User:</th><td> |
| 1727 | 1734 | hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
| 1728 | 1735 | @ </table> |
| 1729 | 1736 | free(zDate); |
| 1737 | + free(zTktTitle); | |
| 1730 | 1738 | |
| 1731 | 1739 | if( g.perm.ModTkt && modPending ){ |
| 1732 | 1740 | @ <div class="section">Moderation</div> |
| 1733 | 1741 | @ <blockquote> |
| 1734 | 1742 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 1735 | 1743 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1665,11 +1665,11 @@ | |
| 1665 | const char *zUuid; |
| 1666 | char zTktName[UUID_SIZE+1]; |
| 1667 | Manifest *pTktChng; |
| 1668 | int modPending; |
| 1669 | const char *zModAction; |
| 1670 | |
| 1671 | login_check_credentials(); |
| 1672 | if( !g.perm.RdTkt ){ login_needed(); return; } |
| 1673 | rid = name_to_rid_www("name"); |
| 1674 | if( rid==0 ){ fossil_redirect_home(); } |
| 1675 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1694,10 +1694,13 @@ | |
| 1694 | } |
| 1695 | if( strcmp(zModAction,"approve")==0 ){ |
| 1696 | moderation_approve(rid); |
| 1697 | } |
| 1698 | } |
| 1699 | style_header("Ticket Change Details"); |
| 1700 | style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid); |
| 1701 | style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); |
| 1702 | style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); |
| 1703 | style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); |
| @@ -1718,17 +1721,22 @@ | |
| 1718 | modPending = moderation_pending(rid); |
| 1719 | if( modPending ){ |
| 1720 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 1721 | } |
| 1722 | @ <tr><th>Ticket:</th> |
| 1723 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr> |
| 1724 | @ <tr><th>Date:</th><td> |
| 1725 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1726 | @ <tr><th>User:</th><td> |
| 1727 | hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
| 1728 | @ </table> |
| 1729 | free(zDate); |
| 1730 | |
| 1731 | if( g.perm.ModTkt && modPending ){ |
| 1732 | @ <div class="section">Moderation</div> |
| 1733 | @ <blockquote> |
| 1734 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 1735 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1665,11 +1665,11 @@ | |
| 1665 | const char *zUuid; |
| 1666 | char zTktName[UUID_SIZE+1]; |
| 1667 | Manifest *pTktChng; |
| 1668 | int modPending; |
| 1669 | const char *zModAction; |
| 1670 | char *zTktTitle; |
| 1671 | login_check_credentials(); |
| 1672 | if( !g.perm.RdTkt ){ login_needed(); return; } |
| 1673 | rid = name_to_rid_www("name"); |
| 1674 | if( rid==0 ){ fossil_redirect_home(); } |
| 1675 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1694,10 +1694,13 @@ | |
| 1694 | } |
| 1695 | if( strcmp(zModAction,"approve")==0 ){ |
| 1696 | moderation_approve(rid); |
| 1697 | } |
| 1698 | } |
| 1699 | zTktTitle = db_table_has_column( "ticket", "title" ) |
| 1700 | ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName) |
| 1701 | : 0; |
| 1702 | style_header("Ticket Change Details"); |
| 1703 | style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid); |
| 1704 | style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); |
| 1705 | style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); |
| 1706 | style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); |
| @@ -1718,17 +1721,22 @@ | |
| 1721 | modPending = moderation_pending(rid); |
| 1722 | if( modPending ){ |
| 1723 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 1724 | } |
| 1725 | @ <tr><th>Ticket:</th> |
| 1726 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> |
| 1727 | if(zTktTitle){ |
| 1728 | @<br>%h(zTktTitle) |
| 1729 | } |
| 1730 | @</td></tr> |
| 1731 | @ <tr><th>Date:</th><td> |
| 1732 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1733 | @ <tr><th>User:</th><td> |
| 1734 | hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
| 1735 | @ </table> |
| 1736 | free(zDate); |
| 1737 | free(zTktTitle); |
| 1738 | |
| 1739 | if( g.perm.ModTkt && modPending ){ |
| 1740 | @ <div class="section">Moderation</div> |
| 1741 | @ <blockquote> |
| 1742 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 1743 |
+10
-2
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1665,11 +1665,11 @@ | ||
| 1665 | 1665 | const char *zUuid; |
| 1666 | 1666 | char zTktName[UUID_SIZE+1]; |
| 1667 | 1667 | Manifest *pTktChng; |
| 1668 | 1668 | int modPending; |
| 1669 | 1669 | const char *zModAction; |
| 1670 | - | |
| 1670 | + char *zTktTitle; | |
| 1671 | 1671 | login_check_credentials(); |
| 1672 | 1672 | if( !g.perm.RdTkt ){ login_needed(); return; } |
| 1673 | 1673 | rid = name_to_rid_www("name"); |
| 1674 | 1674 | if( rid==0 ){ fossil_redirect_home(); } |
| 1675 | 1675 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1694,10 +1694,13 @@ | ||
| 1694 | 1694 | } |
| 1695 | 1695 | if( strcmp(zModAction,"approve")==0 ){ |
| 1696 | 1696 | moderation_approve(rid); |
| 1697 | 1697 | } |
| 1698 | 1698 | } |
| 1699 | + zTktTitle = db_table_has_column( "ticket", "title" ) | |
| 1700 | + ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName) | |
| 1701 | + : 0; | |
| 1699 | 1702 | style_header("Ticket Change Details"); |
| 1700 | 1703 | style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid); |
| 1701 | 1704 | style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); |
| 1702 | 1705 | style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); |
| 1703 | 1706 | style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); |
| @@ -1718,17 +1721,22 @@ | ||
| 1718 | 1721 | modPending = moderation_pending(rid); |
| 1719 | 1722 | if( modPending ){ |
| 1720 | 1723 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 1721 | 1724 | } |
| 1722 | 1725 | @ <tr><th>Ticket:</th> |
| 1723 | - @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr> | |
| 1726 | + @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> | |
| 1727 | + if(zTktTitle){ | |
| 1728 | + @<br>%h(zTktTitle) | |
| 1729 | + } | |
| 1730 | + @</td></tr> | |
| 1724 | 1731 | @ <tr><th>Date:</th><td> |
| 1725 | 1732 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1726 | 1733 | @ <tr><th>User:</th><td> |
| 1727 | 1734 | hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
| 1728 | 1735 | @ </table> |
| 1729 | 1736 | free(zDate); |
| 1737 | + free(zTktTitle); | |
| 1730 | 1738 | |
| 1731 | 1739 | if( g.perm.ModTkt && modPending ){ |
| 1732 | 1740 | @ <div class="section">Moderation</div> |
| 1733 | 1741 | @ <blockquote> |
| 1734 | 1742 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 1735 | 1743 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1665,11 +1665,11 @@ | |
| 1665 | const char *zUuid; |
| 1666 | char zTktName[UUID_SIZE+1]; |
| 1667 | Manifest *pTktChng; |
| 1668 | int modPending; |
| 1669 | const char *zModAction; |
| 1670 | |
| 1671 | login_check_credentials(); |
| 1672 | if( !g.perm.RdTkt ){ login_needed(); return; } |
| 1673 | rid = name_to_rid_www("name"); |
| 1674 | if( rid==0 ){ fossil_redirect_home(); } |
| 1675 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1694,10 +1694,13 @@ | |
| 1694 | } |
| 1695 | if( strcmp(zModAction,"approve")==0 ){ |
| 1696 | moderation_approve(rid); |
| 1697 | } |
| 1698 | } |
| 1699 | style_header("Ticket Change Details"); |
| 1700 | style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid); |
| 1701 | style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); |
| 1702 | style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); |
| 1703 | style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); |
| @@ -1718,17 +1721,22 @@ | |
| 1718 | modPending = moderation_pending(rid); |
| 1719 | if( modPending ){ |
| 1720 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 1721 | } |
| 1722 | @ <tr><th>Ticket:</th> |
| 1723 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr> |
| 1724 | @ <tr><th>Date:</th><td> |
| 1725 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1726 | @ <tr><th>User:</th><td> |
| 1727 | hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
| 1728 | @ </table> |
| 1729 | free(zDate); |
| 1730 | |
| 1731 | if( g.perm.ModTkt && modPending ){ |
| 1732 | @ <div class="section">Moderation</div> |
| 1733 | @ <blockquote> |
| 1734 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 1735 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1665,11 +1665,11 @@ | |
| 1665 | const char *zUuid; |
| 1666 | char zTktName[UUID_SIZE+1]; |
| 1667 | Manifest *pTktChng; |
| 1668 | int modPending; |
| 1669 | const char *zModAction; |
| 1670 | char *zTktTitle; |
| 1671 | login_check_credentials(); |
| 1672 | if( !g.perm.RdTkt ){ login_needed(); return; } |
| 1673 | rid = name_to_rid_www("name"); |
| 1674 | if( rid==0 ){ fossil_redirect_home(); } |
| 1675 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1694,10 +1694,13 @@ | |
| 1694 | } |
| 1695 | if( strcmp(zModAction,"approve")==0 ){ |
| 1696 | moderation_approve(rid); |
| 1697 | } |
| 1698 | } |
| 1699 | zTktTitle = db_table_has_column( "ticket", "title" ) |
| 1700 | ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName) |
| 1701 | : 0; |
| 1702 | style_header("Ticket Change Details"); |
| 1703 | style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid); |
| 1704 | style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); |
| 1705 | style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); |
| 1706 | style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); |
| @@ -1718,17 +1721,22 @@ | |
| 1721 | modPending = moderation_pending(rid); |
| 1722 | if( modPending ){ |
| 1723 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 1724 | } |
| 1725 | @ <tr><th>Ticket:</th> |
| 1726 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> |
| 1727 | if(zTktTitle){ |
| 1728 | @<br>%h(zTktTitle) |
| 1729 | } |
| 1730 | @</td></tr> |
| 1731 | @ <tr><th>Date:</th><td> |
| 1732 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1733 | @ <tr><th>User:</th><td> |
| 1734 | hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
| 1735 | @ </table> |
| 1736 | free(zDate); |
| 1737 | free(zTktTitle); |
| 1738 | |
| 1739 | if( g.perm.ModTkt && modPending ){ |
| 1740 | @ <div class="section">Moderation</div> |
| 1741 | @ <blockquote> |
| 1742 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 1743 |
+56
| --- src/rss.c | ||
| +++ src/rss.c | ||
| @@ -22,18 +22,37 @@ | ||
| 22 | 22 | #include "rss.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | 25 | /* |
| 26 | 26 | ** WEBPAGE: timeline.rss |
| 27 | +** URL: /timeline.rss/y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME | |
| 28 | +** | |
| 29 | +** Produce an RSS feed of the timeline. | |
| 30 | +** | |
| 31 | +** TYPE may be: all, ci (show checkins only), t (show tickets only), | |
| 32 | +** w (show wiki only). LIMIT is the number of items to show. | |
| 33 | +** | |
| 34 | +** tkt=UUID filters for only those events for the specified ticket. tag=TAG | |
| 35 | +** filters for a tag, and wiki=NAME for a wiki page. Only one may be used. | |
| 36 | +** | |
| 37 | +** In addition, name=FILENAME filters for a specific file. This may be | |
| 38 | +** combined with one of the other filters (useful for looking at a specific | |
| 39 | +** branch). | |
| 27 | 40 | */ |
| 41 | + | |
| 28 | 42 | void page_timeline_rss(void){ |
| 29 | 43 | Stmt q; |
| 30 | 44 | int nLine=0; |
| 31 | 45 | char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; |
| 32 | 46 | Blob bSQL; |
| 33 | 47 | const char *zType = PD("y","all"); /* Type of events. All if NULL */ |
| 48 | + const char *zTicketUuid = PD("tkt",NULL); | |
| 49 | + const char *zTag = PD("tag",NULL); | |
| 50 | + const char *zFilename = PD("name",NULL); | |
| 51 | + const char *zWiki = PD("wiki",NULL); | |
| 34 | 52 | int nLimit = atoi(PD("n","20")); |
| 53 | + int nTagId; | |
| 35 | 54 | const char zSQL1[] = |
| 36 | 55 | @ SELECT |
| 37 | 56 | @ blob.rid, |
| 38 | 57 | @ uuid, |
| 39 | 58 | @ event.mtime, |
| @@ -62,10 +81,11 @@ | ||
| 62 | 81 | if( !g.perm.Read ){ |
| 63 | 82 | if( g.perm.RdTkt && g.perm.RdWiki ){ |
| 64 | 83 | blob_append(&bSQL, " AND event.type!='ci'", -1); |
| 65 | 84 | }else if( g.perm.RdTkt ){ |
| 66 | 85 | blob_append(&bSQL, " AND event.type=='t'", -1); |
| 86 | + | |
| 67 | 87 | }else{ |
| 68 | 88 | blob_append(&bSQL, " AND event.type=='w'", -1); |
| 69 | 89 | } |
| 70 | 90 | }else if( !g.perm.RdWiki ){ |
| 71 | 91 | if( g.perm.RdTkt ){ |
| @@ -76,10 +96,46 @@ | ||
| 76 | 96 | }else if( !g.perm.RdTkt ){ |
| 77 | 97 | assert( !g.perm.RdTkt &&& g.perm.Read && g.perm.RdWiki ); |
| 78 | 98 | blob_append(&bSQL, " AND event.type!='t'", -1); |
| 79 | 99 | } |
| 80 | 100 | } |
| 101 | + | |
| 102 | + if( zTicketUuid ){ | |
| 103 | + nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", | |
| 104 | + zTicketUuid); | |
| 105 | + if ( nTagId==0 ){ | |
| 106 | + nTagId = -1; | |
| 107 | + } | |
| 108 | + }else if( zTag ){ | |
| 109 | + nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q*'", | |
| 110 | + zTag); | |
| 111 | + if ( nTagId==0 ){ | |
| 112 | + nTagId = -1; | |
| 113 | + } | |
| 114 | + }else if( zWiki ){ | |
| 115 | + nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'wiki-%q*'", | |
| 116 | + zWiki); | |
| 117 | + if ( nTagId==0 ){ | |
| 118 | + nTagId = -1; | |
| 119 | + } | |
| 120 | + }else{ | |
| 121 | + nTagId = 0; | |
| 122 | + } | |
| 123 | + | |
| 124 | + if( nTagId==-1 ){ | |
| 125 | + blob_appendf(&bSQL, " AND 0"); | |
| 126 | + }else if( nTagId!=0 ){ | |
| 127 | + blob_appendf(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref" | |
| 128 | + " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId); | |
| 129 | + } | |
| 130 | + | |
| 131 | + if( zFilename ){ | |
| 132 | + blob_appendf(&bSQL, | |
| 133 | + " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)", | |
| 134 | + zFilename, filename_collation() | |
| 135 | + ); | |
| 136 | + } | |
| 81 | 137 | |
| 82 | 138 | blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
| 83 | 139 | |
| 84 | 140 | cgi_set_content_type("application/rss+xml"); |
| 85 | 141 | |
| 86 | 142 |
| --- src/rss.c | |
| +++ src/rss.c | |
| @@ -22,18 +22,37 @@ | |
| 22 | #include "rss.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** WEBPAGE: timeline.rss |
| 27 | */ |
| 28 | void page_timeline_rss(void){ |
| 29 | Stmt q; |
| 30 | int nLine=0; |
| 31 | char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; |
| 32 | Blob bSQL; |
| 33 | const char *zType = PD("y","all"); /* Type of events. All if NULL */ |
| 34 | int nLimit = atoi(PD("n","20")); |
| 35 | const char zSQL1[] = |
| 36 | @ SELECT |
| 37 | @ blob.rid, |
| 38 | @ uuid, |
| 39 | @ event.mtime, |
| @@ -62,10 +81,11 @@ | |
| 62 | if( !g.perm.Read ){ |
| 63 | if( g.perm.RdTkt && g.perm.RdWiki ){ |
| 64 | blob_append(&bSQL, " AND event.type!='ci'", -1); |
| 65 | }else if( g.perm.RdTkt ){ |
| 66 | blob_append(&bSQL, " AND event.type=='t'", -1); |
| 67 | }else{ |
| 68 | blob_append(&bSQL, " AND event.type=='w'", -1); |
| 69 | } |
| 70 | }else if( !g.perm.RdWiki ){ |
| 71 | if( g.perm.RdTkt ){ |
| @@ -76,10 +96,46 @@ | |
| 76 | }else if( !g.perm.RdTkt ){ |
| 77 | assert( !g.perm.RdTkt &&& g.perm.Read && g.perm.RdWiki ); |
| 78 | blob_append(&bSQL, " AND event.type!='t'", -1); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
| 83 | |
| 84 | cgi_set_content_type("application/rss+xml"); |
| 85 | |
| 86 |
| --- src/rss.c | |
| +++ src/rss.c | |
| @@ -22,18 +22,37 @@ | |
| 22 | #include "rss.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** WEBPAGE: timeline.rss |
| 27 | ** URL: /timeline.rss/y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME |
| 28 | ** |
| 29 | ** Produce an RSS feed of the timeline. |
| 30 | ** |
| 31 | ** TYPE may be: all, ci (show checkins only), t (show tickets only), |
| 32 | ** w (show wiki only). LIMIT is the number of items to show. |
| 33 | ** |
| 34 | ** tkt=UUID filters for only those events for the specified ticket. tag=TAG |
| 35 | ** filters for a tag, and wiki=NAME for a wiki page. Only one may be used. |
| 36 | ** |
| 37 | ** In addition, name=FILENAME filters for a specific file. This may be |
| 38 | ** combined with one of the other filters (useful for looking at a specific |
| 39 | ** branch). |
| 40 | */ |
| 41 | |
| 42 | void page_timeline_rss(void){ |
| 43 | Stmt q; |
| 44 | int nLine=0; |
| 45 | char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; |
| 46 | Blob bSQL; |
| 47 | const char *zType = PD("y","all"); /* Type of events. All if NULL */ |
| 48 | const char *zTicketUuid = PD("tkt",NULL); |
| 49 | const char *zTag = PD("tag",NULL); |
| 50 | const char *zFilename = PD("name",NULL); |
| 51 | const char *zWiki = PD("wiki",NULL); |
| 52 | int nLimit = atoi(PD("n","20")); |
| 53 | int nTagId; |
| 54 | const char zSQL1[] = |
| 55 | @ SELECT |
| 56 | @ blob.rid, |
| 57 | @ uuid, |
| 58 | @ event.mtime, |
| @@ -62,10 +81,11 @@ | |
| 81 | if( !g.perm.Read ){ |
| 82 | if( g.perm.RdTkt && g.perm.RdWiki ){ |
| 83 | blob_append(&bSQL, " AND event.type!='ci'", -1); |
| 84 | }else if( g.perm.RdTkt ){ |
| 85 | blob_append(&bSQL, " AND event.type=='t'", -1); |
| 86 | |
| 87 | }else{ |
| 88 | blob_append(&bSQL, " AND event.type=='w'", -1); |
| 89 | } |
| 90 | }else if( !g.perm.RdWiki ){ |
| 91 | if( g.perm.RdTkt ){ |
| @@ -76,10 +96,46 @@ | |
| 96 | }else if( !g.perm.RdTkt ){ |
| 97 | assert( !g.perm.RdTkt &&& g.perm.Read && g.perm.RdWiki ); |
| 98 | blob_append(&bSQL, " AND event.type!='t'", -1); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | if( zTicketUuid ){ |
| 103 | nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", |
| 104 | zTicketUuid); |
| 105 | if ( nTagId==0 ){ |
| 106 | nTagId = -1; |
| 107 | } |
| 108 | }else if( zTag ){ |
| 109 | nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q*'", |
| 110 | zTag); |
| 111 | if ( nTagId==0 ){ |
| 112 | nTagId = -1; |
| 113 | } |
| 114 | }else if( zWiki ){ |
| 115 | nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'wiki-%q*'", |
| 116 | zWiki); |
| 117 | if ( nTagId==0 ){ |
| 118 | nTagId = -1; |
| 119 | } |
| 120 | }else{ |
| 121 | nTagId = 0; |
| 122 | } |
| 123 | |
| 124 | if( nTagId==-1 ){ |
| 125 | blob_appendf(&bSQL, " AND 0"); |
| 126 | }else if( nTagId!=0 ){ |
| 127 | blob_appendf(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref" |
| 128 | " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId); |
| 129 | } |
| 130 | |
| 131 | if( zFilename ){ |
| 132 | blob_appendf(&bSQL, |
| 133 | " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)", |
| 134 | zFilename, filename_collation() |
| 135 | ); |
| 136 | } |
| 137 | |
| 138 | blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
| 139 | |
| 140 | cgi_set_content_type("application/rss+xml"); |
| 141 | |
| 142 |