/[LeafOK_CVS]/lbbs/src/section_list_display.c
ViewVC logotype

Annotation of /lbbs/src/section_list_display.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.63 - (hide annotations)
Mon Nov 3 02:32:11 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.62: +12 -0 lines
Content type: text/x-csrc
Add user search in section list display

1 sysadm 1.1 /***************************************************************************
2     section_list_display.c - description
3     -------------------
4     Copyright : (C) 2004-2025 by Leaflet
5     Email : leaflet@leafok.com
6     ***************************************************************************/
7    
8     /***************************************************************************
9     * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 3 of the License, or *
13     * (at your option) any later version. *
14     * *
15     ***************************************************************************/
16    
17 sysadm 1.9 #include "article_cache.h"
18 sysadm 1.53 #include "article_favor.h"
19 sysadm 1.41 #include "article_op.h"
20 sysadm 1.20 #include "article_post.h"
21 sysadm 1.34 #include "article_view_log.h"
22 sysadm 1.26 #include "article_del.h"
23 sysadm 1.1 #include "common.h"
24     #include "io.h"
25 sysadm 1.34 #include "log.h"
26     #include "login.h"
27 sysadm 1.37 #include "menu.h"
28 sysadm 1.48 #include "menu_proc.h"
29 sysadm 1.34 #include "section_list_display.h"
30     #include "section_list_loader.h"
31 sysadm 1.1 #include "screen.h"
32 sysadm 1.34 #include "str_process.h"
33 sysadm 1.57 #include "user_info_display.h"
34 sysadm 1.63 #include "user_list_display.h"
35 sysadm 1.18 #include "user_priv.h"
36 sysadm 1.59 #include <errno.h>
37 sysadm 1.29 #include <string.h>
38 sysadm 1.1 #include <time.h>
39 sysadm 1.6 #include <sys/param.h>
40 sysadm 1.1
41 sysadm 1.59 static int32_t section_aid_locations[BBS_max_section] = {0};
42 sysadm 1.11 static int section_topic_view_mode = 0;
43 sysadm 1.13 static int section_topic_view_tid = -1;
44 sysadm 1.11
45 sysadm 1.1 enum select_cmd_t
46     {
47     EXIT_SECTION = 0,
48 sysadm 1.41 VIEW_ARTICLE,
49     CHANGE_PAGE,
50     SHOW_HELP,
51     CHANGE_NAME_DISPLAY,
52     POST_ARTICLE,
53     EDIT_ARTICLE,
54     DELETE_ARTICLE,
55     QUERY_ARTICLE,
56 sysadm 1.57 QUERY_USER,
57 sysadm 1.53 SET_FAVOR_ARTICLE,
58     UNSET_FAVOR_ARTICLE,
59 sysadm 1.41 FIRST_TOPIC_ARTICLE,
60     LAST_TOPIC_ARTICLE,
61 sysadm 1.45 SCAN_NEW_ARTICLE,
62 sysadm 1.41 VIEW_EX_DIR,
63 sysadm 1.48 SHOW_TOP10,
64 sysadm 1.63 SEARCH_USER,
65 sysadm 1.1 };
66    
67 sysadm 1.36 static int section_list_draw_items(int page_id, ARTICLE *p_articles[], int article_count, int display_nickname, int ontop_start_offset)
68 sysadm 1.1 {
69     char str_time[LINE_BUFFER_LEN];
70     struct tm tm_sub;
71     char title_f[BBS_article_title_max_len + 1];
72     int title_f_len;
73     int eol;
74     int len;
75     int i;
76 sysadm 1.42 size_t j;
77 sysadm 1.3 char article_flag;
78 sysadm 1.17 int is_viewed;
79 sysadm 1.53 int is_favor;
80 sysadm 1.3 time_t tm_now;
81    
82     time(&tm_now);
83 sysadm 1.1
84     clrline(4, 23);
85    
86     for (i = 0; i < article_count; i++)
87     {
88 sysadm 1.18 if (p_articles[i]->uid == BBS_priv.uid)
89 sysadm 1.17 {
90 sysadm 1.18 is_viewed = 1;
91     }
92     else
93     {
94     is_viewed = article_view_log_is_viewed(p_articles[i]->aid, &BBS_article_view_log);
95     if (is_viewed < 0)
96     {
97     log_error("article_view_log_is_viewed(aid=%d) error\n", p_articles[i]->aid);
98     is_viewed = 0;
99     }
100 sysadm 1.17 }
101    
102 sysadm 1.53 if (p_articles[i]->tid == 0)
103     {
104     is_favor = article_favor_check(p_articles[i]->aid, &BBS_article_favor);
105     if (is_favor < 0)
106     {
107     log_error("article_favor_check(aid=%d) error\n", p_articles[i]->aid);
108     is_favor = 0;
109     }
110     }
111     else
112     {
113     is_favor = 0;
114     }
115    
116 sysadm 1.3 if (p_articles[i]->excerption)
117     {
118 sysadm 1.17 article_flag = (is_viewed ? 'm' : 'M');
119 sysadm 1.3 }
120 sysadm 1.17 else if (p_articles[i]->lock && is_viewed)
121 sysadm 1.3 {
122     article_flag = 'x';
123     }
124 sysadm 1.35 else
125     {
126     article_flag = (is_viewed ? ' ' : 'N');
127     }
128 sysadm 1.3
129 sysadm 1.1 localtime_r(&p_articles[i]->sub_dt, &tm_sub);
130 sysadm 1.3 if (tm_now - p_articles[i]->sub_dt < 3600 * 24 * 365)
131     {
132     strftime(str_time, sizeof(str_time), "%b %e ", &tm_sub);
133     }
134     else
135     {
136     strftime(str_time, sizeof(str_time), "%m/%Y", &tm_sub);
137     }
138    
139 sysadm 1.40 strncpy(title_f, (p_articles[i]->tid == 0 ? "● " : ""), sizeof(title_f) - 1);
140 sysadm 1.1 title_f[sizeof(title_f) - 1] = '\0';
141 sysadm 1.40 strncat(title_f, (p_articles[i]->transship ? "[转载]" : ""), sizeof(title_f) - 1 - strnlen(title_f, sizeof(title_f)));
142 sysadm 1.42
143     // Rewrite title with "Re: Re: " prefix into "Re: ... "
144     j = 0;
145     if (p_articles[i]->tid != 0)
146     {
147     while (strncmp(p_articles[i]->title + j, "Re: ", strlen("Re: ")) == 0)
148     {
149     j += strlen("Re: ");
150     }
151     if (j >= strlen("Re: Re: "))
152     {
153     strncat(title_f, "Re: ... ", sizeof(title_f) - 1 - strnlen(title_f, sizeof(title_f)));
154     }
155     else
156     {
157     j = 0;
158     }
159     }
160     strncat(title_f, p_articles[i]->title + j, sizeof(title_f) - 1 - strnlen(title_f, sizeof(title_f)));
161    
162 sysadm 1.51 len = split_line(title_f, 59 - (display_nickname ? BBS_nickname_max_len / 2 : BBS_username_max_len), &eol, &title_f_len, 1);
163 sysadm 1.1 if (title_f[len] != '\0')
164     {
165     title_f[len] = '\0';
166     }
167    
168     moveto(4 + i, 1);
169 sysadm 1.36 if (i >= ontop_start_offset)
170     {
171 sysadm 1.53 prints(" \033[1;33m[提示]\033[m%c%c %s%*s %s %s%s\033[m",
172     (is_favor ? '@' : ' '),
173 sysadm 1.36 article_flag,
174     (display_nickname ? p_articles[i]->nickname : p_articles[i]->username),
175 sysadm 1.43 (display_nickname ? BBS_nickname_max_len / 2 - str_length(p_articles[i]->nickname, 1)
176     : BBS_username_max_len - str_length(p_articles[i]->username, 1)),
177 sysadm 1.36 "",
178     str_time,
179     (p_articles[i]->aid == section_topic_view_tid
180     ? "\033[1;33m"
181     : (p_articles[i]->tid == section_topic_view_tid
182     ? "\033[1;36m"
183     : "")),
184     title_f);
185     }
186     else
187     {
188 sysadm 1.53 prints(" %s%7d\033[m%c%c %s%*s %s %s%s\033[m",
189 sysadm 1.36 (p_articles[i]->aid == section_topic_view_tid
190     ? "\033[1;33m"
191     : (p_articles[i]->tid == section_topic_view_tid
192     ? "\033[1;36m"
193     : "")),
194     p_articles[i]->aid,
195 sysadm 1.53 (is_favor ? '@' : ' '),
196 sysadm 1.36 article_flag,
197     (display_nickname ? p_articles[i]->nickname : p_articles[i]->username),
198 sysadm 1.43 (display_nickname ? BBS_nickname_max_len / 2 - str_length(p_articles[i]->nickname, 1)
199     : BBS_username_max_len - str_length(p_articles[i]->username, 1)),
200 sysadm 1.36 "",
201     str_time,
202     (p_articles[i]->aid == section_topic_view_tid
203     ? "\033[1;33m"
204     : (p_articles[i]->tid == section_topic_view_tid
205     ? "\033[1;36m"
206     : "")),
207     title_f);
208     }
209 sysadm 1.1 }
210    
211     return 0;
212     }
213    
214 sysadm 1.5 static int section_list_draw_screen(const char *sname, const char *stitle, const char *master_list, int display_nickname)
215 sysadm 1.1 {
216 sysadm 1.40 char str_section_master[LINE_BUFFER_LEN] = "诚征版主中";
217 sysadm 1.1 char str_section_name[LINE_BUFFER_LEN];
218    
219 sysadm 1.5 if (master_list[0] != '\0')
220 sysadm 1.1 {
221 sysadm 1.40 snprintf(str_section_master, sizeof(str_section_master), "版主:%s", master_list);
222 sysadm 1.1 }
223 sysadm 1.40 snprintf(str_section_name, sizeof(str_section_name), "讨论区 [%s]", sname);
224 sysadm 1.1
225     clearscr();
226 sysadm 1.5 show_top(str_section_master, stitle, str_section_name);
227 sysadm 1.1 moveto(2, 0);
228 sysadm 1.40 prints("返回[\033[1;32m←\033[0;37m,\033[1;32mESC\033[0;37m] 选择[\033[1;32m↑\033[0;37m,\033[1;32m↓\033[0;37m] "
229     "阅读[\033[1;32m→\033[0;37m,\033[1;32mENTER\033[0;37m] 发表[\033[1;32mCtrl-P\033[0;37m] "
230     "%s[\033[1;32mn\033[0;37m] 精华区[\033[1;32mx\033[0;37m] 帮助[\033[1;32mh\033[0;37m]\033[m",
231     (display_nickname ? "用户名" : "昵称"));
232 sysadm 1.1 moveto(3, 0);
233 sysadm 1.4 if (display_nickname)
234     {
235 sysadm 1.40 prints("\033[44;37m \033[1;37m 编 号 发布者昵称 日 期 文章标题 \033[m");
236 sysadm 1.4 }
237     else
238     {
239 sysadm 1.40 prints("\033[44;37m \033[1;37m 编 号 发 布 者 日 期 文章标题 \033[m");
240 sysadm 1.4 }
241 sysadm 1.1
242     return 0;
243     }
244    
245     static enum select_cmd_t section_list_select(int total_page, int item_count, int *p_page_id, int *p_selected_index)
246     {
247     int old_page_id = *p_page_id;
248     int old_selected_index = *p_selected_index;
249     int ch;
250 sysadm 1.30 time_t last_refresh_tm = time(NULL);
251 sysadm 1.1
252     if (item_count > 0 && *p_selected_index >= 0)
253     {
254     moveto(4 + *p_selected_index, 1);
255     outc('>');
256     iflush();
257     }
258    
259     while (!SYS_server_exit)
260     {
261     ch = igetch(100);
262    
263 sysadm 1.54 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
264     {
265     BBS_last_access_tm = time(NULL);
266     }
267    
268 sysadm 1.1 switch (ch)
269     {
270 sysadm 1.57 case KEY_NULL: // broken pipe
271 sysadm 1.54 log_error("KEY_NULL\n");
272     return EXIT_SECTION;
273 sysadm 1.1 case KEY_TIMEOUT:
274 sysadm 1.30 if (time(NULL) - BBS_last_access_tm >= MAX_DELAY_TIME)
275 sysadm 1.1 {
276 sysadm 1.54 log_error("User input timeout\n");
277     return EXIT_SECTION;
278 sysadm 1.1 }
279     continue;
280 sysadm 1.54 case KEY_ESC:
281     case KEY_LEFT:
282     return EXIT_SECTION;
283 sysadm 1.4 case 'n':
284     return CHANGE_NAME_DISPLAY;
285 sysadm 1.1 case CR:
286 sysadm 1.28 case 'r':
287 sysadm 1.1 case KEY_RIGHT:
288     if (item_count > 0)
289     {
290     return VIEW_ARTICLE;
291     }
292     break;
293 sysadm 1.20 case Ctrl('P'):
294     return POST_ARTICLE;
295     case 'E':
296 sysadm 1.41 if (item_count > 0)
297     {
298     return EDIT_ARTICLE;
299     }
300     break;
301 sysadm 1.26 case 'd':
302 sysadm 1.41 if (item_count > 0)
303     {
304     return DELETE_ARTICLE;
305     }
306     break;
307     case Ctrl('Q'):
308     if (item_count > 0)
309     {
310     return QUERY_ARTICLE;
311     }
312     break;
313 sysadm 1.57 case Ctrl('A'):
314     if (item_count > 0)
315     {
316     return QUERY_USER;
317     }
318     break;
319 sysadm 1.53 case 'F':
320     if (item_count > 0)
321     {
322     return SET_FAVOR_ARTICLE;
323     }
324     break;
325     case '-':
326     if (item_count > 0)
327     {
328     return UNSET_FAVOR_ARTICLE;
329     }
330     break;
331 sysadm 1.1 case KEY_HOME:
332     *p_page_id = 0;
333 sysadm 1.28 case 'P':
334 sysadm 1.1 case KEY_PGUP:
335     *p_selected_index = 0;
336 sysadm 1.28 case 'k':
337 sysadm 1.1 case KEY_UP:
338     if (*p_selected_index <= 0)
339     {
340     if (*p_page_id > 0)
341     {
342     (*p_page_id)--;
343     *p_selected_index = BBS_article_limit_per_page - 1;
344     }
345 sysadm 1.44 else if (ch == KEY_UP || ch == 'k') // Rotate to the tail of section list
346     {
347     if (total_page > 0)
348     {
349     *p_page_id = total_page - 1;
350     }
351     if (item_count > 0)
352     {
353     *p_selected_index = item_count - 1;
354     }
355     }
356 sysadm 1.1 }
357     else
358     {
359     (*p_selected_index)--;
360     }
361     break;
362 sysadm 1.28 case '$':
363 sysadm 1.1 case KEY_END:
364     if (total_page > 0)
365     {
366     *p_page_id = total_page - 1;
367     }
368 sysadm 1.28 case 'N':
369 sysadm 1.1 case KEY_PGDN:
370     if (item_count > 0)
371     {
372     *p_selected_index = item_count - 1;
373     }
374 sysadm 1.28 case 'j':
375 sysadm 1.1 case KEY_DOWN:
376     if (*p_selected_index + 1 >= item_count) // next page
377     {
378     if (*p_page_id + 1 < total_page)
379     {
380     (*p_page_id)++;
381     *p_selected_index = 0;
382     }
383 sysadm 1.44 else if (ch == KEY_DOWN || ch == 'j') // Rotate to the head of section list
384 sysadm 1.25 {
385 sysadm 1.44 *p_page_id = 0;
386     *p_selected_index = 0;
387 sysadm 1.25 }
388 sysadm 1.1 }
389     else
390     {
391     (*p_selected_index)++;
392     }
393     break;
394 sysadm 1.31 case '=':
395 sysadm 1.41 if (item_count > 0)
396     {
397     return FIRST_TOPIC_ARTICLE;
398     }
399     break;
400 sysadm 1.31 case '\\':
401 sysadm 1.41 if (item_count > 0)
402     {
403     return LAST_TOPIC_ARTICLE;
404     }
405     break;
406 sysadm 1.45 case 'S':
407     if (item_count > 0)
408     {
409     return SCAN_NEW_ARTICLE;
410     }
411     break;
412 sysadm 1.63 case 'u':
413     return SEARCH_USER;
414 sysadm 1.28 case 'h':
415     return SHOW_HELP;
416 sysadm 1.37 case 'x':
417     return VIEW_EX_DIR;
418 sysadm 1.49 case 'H':
419 sysadm 1.48 return SHOW_TOP10;
420 sysadm 1.1 default:
421 sysadm 1.56 break;
422 sysadm 1.1 }
423    
424     if (old_page_id != *p_page_id)
425     {
426     return CHANGE_PAGE;
427     }
428    
429     if (item_count > 0 && old_selected_index != *p_selected_index)
430     {
431     if (old_selected_index >= 0)
432     {
433     moveto(4 + old_selected_index, 1);
434     outc(' ');
435     }
436     if (*p_selected_index >= 0)
437     {
438     moveto(4 + *p_selected_index, 1);
439     outc('>');
440     }
441     iflush();
442    
443     old_selected_index = *p_selected_index;
444     }
445    
446 sysadm 1.30 if (BBS_last_access_tm - last_refresh_tm >= BBS_section_list_load_interval)
447     {
448     return CHANGE_PAGE; // force section list refresh
449     }
450 sysadm 1.1 }
451    
452     return EXIT_SECTION;
453     }
454    
455 sysadm 1.11 static int display_article_key_handler(int *p_key, DISPLAY_CTX *p_ctx)
456 sysadm 1.10 {
457 sysadm 1.11 switch (*p_key)
458 sysadm 1.10 {
459 sysadm 1.11 case 'p':
460 sysadm 1.28 case Ctrl('X'):
461 sysadm 1.11 section_topic_view_mode = !section_topic_view_mode;
462 sysadm 1.10 case 0: // Set msg
463 sysadm 1.11 if (section_topic_view_mode)
464 sysadm 1.10 {
465 sysadm 1.11 snprintf(p_ctx->msg, sizeof(p_ctx->msg),
466 sysadm 1.40 "| 返回[\033[32m←\033[33m,\033[32mESC\033[33m] "
467     "同主题阅读[\033[32m↑\033[33m/\033[32m↓\033[33m] "
468     "切换[\033[32mp\033[33m] 回复[\033[32mr\033[33m] 帮助[\033[32mh\033[33m] |");
469 sysadm 1.10 }
470     else
471     {
472 sysadm 1.11 snprintf(p_ctx->msg, sizeof(p_ctx->msg),
473 sysadm 1.40 "| 返回[\033[32m←\033[33m,\033[32mESC\033[33m] "
474     "移动[\033[32m↑\033[33m/\033[32m↓\033[33m/\033[32mPgUp\033[33m/\033[32mPgDn\033[33m] "
475     "切换[\033[32mp\033[33m] 回复[\033[32mr\033[33m] 帮助[\033[32mh\033[33m] |");
476 sysadm 1.10 }
477 sysadm 1.11 *p_key = 0;
478 sysadm 1.10 break;
479 sysadm 1.20 case 'r': // Reply article
480     return 1;
481 sysadm 1.31 case '=': // First topic article
482     return 1;
483     case '\\': // Last topic article
484     return 1;
485 sysadm 1.12 case KEY_UP:
486 sysadm 1.16 case KEY_PGUP:
487     case KEY_HOME:
488 sysadm 1.12 if (p_ctx->reach_begin)
489     {
490     if (section_topic_view_mode)
491     {
492     *p_key = KEY_PGUP;
493     }
494 sysadm 1.16 else
495     {
496     *p_key = KEY_UP;
497     }
498 sysadm 1.12 return 1;
499     }
500     break;
501 sysadm 1.28 case 'k':
502     if (section_topic_view_mode)
503     {
504     *p_key = KEY_PGUP;
505     }
506     else
507     {
508     *p_key = KEY_UP;
509     }
510     return 1;
511 sysadm 1.12 case KEY_DOWN:
512 sysadm 1.16 case KEY_PGDN:
513     case KEY_END:
514 sysadm 1.12 if (p_ctx->reach_end)
515     {
516     if (section_topic_view_mode)
517     {
518     *p_key = KEY_PGDN;
519     }
520 sysadm 1.16 else
521     {
522     *p_key = KEY_DOWN;
523     }
524 sysadm 1.12 return 1;
525     }
526     break;
527 sysadm 1.28 case 'j':
528     if (section_topic_view_mode)
529     {
530     *p_key = KEY_PGDN;
531     }
532     else
533     {
534     *p_key = KEY_DOWN;
535     }
536     return 1;
537 sysadm 1.10 }
538    
539     return 0;
540     }
541    
542 sysadm 1.46 int section_list_display(const char *sname, int32_t aid)
543 sysadm 1.1 {
544 sysadm 1.4 static int display_nickname = 0;
545    
546 sysadm 1.1 SECTION_LIST *p_section;
547 sysadm 1.46 int64_t section_index;
548     int32_t aid_location;
549 sysadm 1.5 char stitle[BBS_section_title_max_len + 1];
550     char master_list[(BBS_username_max_len + 1) * 3 + 1];
551 sysadm 1.6 char page_info_str[LINE_BUFFER_LEN];
552 sysadm 1.1 ARTICLE *p_articles[BBS_article_limit_per_page];
553     int article_count;
554 sysadm 1.6 int page_count;
555 sysadm 1.36 int ontop_start_offset;
556 sysadm 1.1 int page_id = 0;
557     int selected_index = 0;
558 sysadm 1.10 ARTICLE_CACHE cache;
559 sysadm 1.1 int ret;
560 sysadm 1.12 int loop;
561     int direction;
562 sysadm 1.24 ARTICLE article_new;
563 sysadm 1.31 int page_id_cur;
564 sysadm 1.46 const ARTICLE *p_article_locate;
565 sysadm 1.57 USER_INFO user_info;
566 sysadm 1.61 char user_intro[BBS_user_intro_max_len];
567 sysadm 1.1
568 sysadm 1.50 p_section = section_list_find_by_name(sname);
569 sysadm 1.1 if (p_section == NULL)
570     {
571     log_error("Section %s not found\n", sname);
572     return -1;
573     }
574    
575 sysadm 1.52 if (!checkpriv(&BBS_priv, p_section->sid, S_LIST))
576     {
577     log_error("Forbid access to unauthorized section, sid=%d, uid=%d\n",
578     p_section->sid, BBS_priv.uid);
579     return -1;
580     }
581    
582 sysadm 1.50 section_index = get_section_index(p_section);
583    
584 sysadm 1.58 if (get_section_info(p_section, NULL, stitle, master_list) < 0)
585 sysadm 1.5 {
586 sysadm 1.58 log_error("get_section_info(sid=%d) error\n", p_section->sid);
587     return -4;
588 sysadm 1.5 }
589    
590 sysadm 1.46 if (aid == 0)
591     {
592     aid_location = section_aid_locations[section_index];
593     }
594     else
595     {
596     aid_location = aid;
597 sysadm 1.51 }
598 sysadm 1.46
599     // Locate at article with aid_locate
600     if (aid_location > 0)
601     {
602     p_article_locate = article_block_find_by_aid(aid_location);
603     if (p_article_locate == NULL)
604     {
605     log_error("article_block_find_by_aid(%d) error\n", aid_location);
606     return -3;
607     }
608    
609     ret = locate_article_in_section(p_section, p_article_locate, 0, 0,
610     &page_id, &selected_index, &article_count);
611     if (ret < 0)
612     {
613     log_error("locate_article_in_section(sid=%d, aid=%d, direction=0, step=0) error\n",
614     p_section->sid, p_article_locate->aid);
615     return -3;
616     }
617     }
618    
619 sysadm 1.5 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
620 sysadm 1.1 {
621     log_error("section_list_draw_screen() error\n");
622     return -2;
623     }
624    
625 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
626 sysadm 1.5 if (ret < 0)
627 sysadm 1.1 {
628 sysadm 1.5 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
629     return -3;
630 sysadm 1.1 }
631 sysadm 1.5
632 sysadm 1.46 section_topic_view_tid = -1;
633    
634 sysadm 1.5 if (article_count == 0) // empty section
635 sysadm 1.1 {
636     selected_index = 0;
637     }
638 sysadm 1.46 else if (aid > 0)
639     {
640     // Update current topic
641     section_topic_view_tid = (p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
642    
643     // Update current aid location
644     section_aid_locations[section_index] = p_articles[selected_index]->aid;
645     }
646 sysadm 1.1
647     while (!SYS_server_exit)
648     {
649 sysadm 1.36 ret = section_list_draw_items(page_id, p_articles, article_count, display_nickname, ontop_start_offset);
650 sysadm 1.1 if (ret < 0)
651     {
652     log_error("section_list_draw_items(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
653     return -4;
654     }
655 sysadm 1.6
656 sysadm 1.7 snprintf(page_info_str, sizeof(page_info_str),
657 sysadm 1.40 "\033[33m[第\033[36m%d\033[33m/\033[36m%d\033[33m页]",
658 sysadm 1.7 page_id + 1, MAX(page_count, 1));
659 sysadm 1.8
660 sysadm 1.6 show_bottom(page_info_str);
661 sysadm 1.1 iflush();
662    
663 sysadm 1.33 if (user_online_update(sname) < 0)
664     {
665     log_error("user_online_update(%s) error\n", sname);
666     }
667    
668 sysadm 1.6 ret = section_list_select(page_count, article_count, &page_id, &selected_index);
669 sysadm 1.60
670 sysadm 1.1 switch (ret)
671     {
672     case EXIT_SECTION:
673 sysadm 1.62 // Update current aid location
674     if (p_articles[selected_index] != NULL)
675     {
676     section_aid_locations[section_index] = p_articles[selected_index]->aid;
677     }
678     else
679     {
680     log_error("p_articles[selected_index=%d] is NULL when exit section [%s]\n", selected_index, sname);
681     }
682 sysadm 1.1 return 0;
683     case CHANGE_PAGE:
684 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
685 sysadm 1.5 if (ret < 0)
686 sysadm 1.1 {
687 sysadm 1.5 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
688     return -3;
689 sysadm 1.1 }
690 sysadm 1.5 if (article_count == 0) // empty section
691 sysadm 1.1 {
692     selected_index = 0;
693     }
694 sysadm 1.5 else if (selected_index >= article_count)
695 sysadm 1.1 {
696     selected_index = article_count - 1;
697     }
698     break;
699     case VIEW_ARTICLE:
700 sysadm 1.12 do
701 sysadm 1.9 {
702 sysadm 1.12 loop = 0;
703 sysadm 1.9
704 sysadm 1.12 if (article_cache_load(&cache, VAR_ARTICLE_CACHE_DIR, p_articles[selected_index]) < 0)
705     {
706     log_error("article_cache_load(aid=%d, cid=%d) error\n", p_articles[selected_index]->aid, p_articles[selected_index]->cid);
707     break;
708     }
709    
710 sysadm 1.33 if (user_online_update("VIEW_ARTICLE") < 0)
711     {
712     log_error("user_online_update(VIEW_ARTICLE) error\n");
713     }
714    
715 sysadm 1.19 ret = display_data(cache.p_data, cache.line_total, cache.line_offsets, 0,
716 sysadm 1.12 display_article_key_handler, DATA_READ_HELP);
717    
718     if (article_cache_unload(&cache) < 0)
719     {
720     log_error("article_cache_unload(aid=%d, cid=%d) error\n", p_articles[selected_index]->aid, p_articles[selected_index]->cid);
721     break;
722     }
723    
724 sysadm 1.17 // Update article_view_log
725     if (article_view_log_set_viewed(p_articles[selected_index]->aid, &BBS_article_view_log) < 0)
726     {
727     log_error("article_view_log_set_viewed(aid=%d) error\n", p_articles[selected_index]->aid);
728     }
729    
730 sysadm 1.12 switch (ret)
731     {
732     case KEY_UP:
733     if (selected_index <= 0)
734     {
735     if (page_id > 0)
736     {
737     page_id--;
738     selected_index = BBS_article_limit_per_page - 1;
739    
740 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
741 sysadm 1.12 if (ret < 0)
742     {
743     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
744     return -3;
745     }
746    
747     if (article_count != BBS_article_limit_per_page) // page is not full
748     {
749     selected_index = MAX(0, article_count - 1);
750     }
751     else
752     {
753     loop = 1;
754     }
755     }
756     }
757     else
758     {
759     selected_index--;
760     loop = 1;
761     }
762     break;
763     case KEY_DOWN:
764     if (selected_index + 1 >= article_count) // next page
765     {
766     if (page_id + 1 < page_count)
767     {
768     page_id++;
769     selected_index = 0;
770    
771 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
772 sysadm 1.12 if (ret < 0)
773     {
774     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
775     return -3;
776     }
777    
778     if (article_count == 0) // empty page
779     {
780     selected_index = 0;
781     }
782     else
783     {
784     loop = 1;
785     }
786     }
787     }
788     else
789     {
790     selected_index++;
791     loop = 1;
792     }
793     break;
794     case KEY_PGUP:
795     case KEY_PGDN:
796     direction = (ret == KEY_PGUP ? -1 : 1);
797 sysadm 1.31 ret = locate_article_in_section(p_section, p_articles[selected_index], direction, 1,
798     &page_id, &selected_index, &article_count);
799 sysadm 1.12 if (ret < 0)
800     {
801 sysadm 1.31 log_error("locate_article_in_section(sid=%d, aid=%d, direction=%d, step=1) error\n",
802 sysadm 1.12 p_section->sid, p_articles[selected_index]->aid, direction);
803     return -3;
804     }
805 sysadm 1.31 else if (ret > 0) // found
806 sysadm 1.12 {
807 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
808 sysadm 1.12 if (ret < 0)
809     {
810     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
811     return -3;
812     }
813     loop = 1;
814     }
815     break;
816 sysadm 1.20 case 'r': // Reply article
817 sysadm 1.33 if (user_online_update("REPLY_ARTICLE") < 0)
818     {
819     log_error("user_online_update(REPLY_ARTICLE) error\n");
820     }
821    
822 sysadm 1.24 if (article_reply(p_section, p_articles[selected_index], &article_new) < 0)
823 sysadm 1.20 {
824 sysadm 1.33 log_error("article_reply(aid=%d) error\n", p_articles[selected_index]->aid);
825 sysadm 1.20 }
826     loop = 1;
827     break;
828 sysadm 1.31 case '=': // First topic article
829     case '\\': // Last topic article
830     page_id_cur = page_id;
831     direction = (ret == '=' ? -1 : 1);
832     ret = locate_article_in_section(p_section, p_articles[selected_index], direction, BBS_article_limit_per_section,
833     &page_id, &selected_index, &article_count);
834     if (ret < 0)
835     {
836     log_error("locate_article_in_section(sid=%d, aid=%d, direction=%d, step=%d) error\n",
837     p_section->sid, p_articles[selected_index]->aid, direction, BBS_article_limit_per_section);
838     return -3;
839     }
840     else if (ret > 0) // found
841     {
842     if (page_id != page_id_cur) // page changed
843     {
844 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
845 sysadm 1.31 if (ret < 0)
846     {
847     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
848     return -3;
849     }
850     }
851     loop = 1;
852     }
853     break;
854 sysadm 1.12 }
855     } while (loop);
856 sysadm 1.13
857     // Update current topic
858     section_topic_view_tid = (p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
859 sysadm 1.46
860 sysadm 1.35 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
861     {
862     log_error("section_list_draw_screen() error\n");
863     return -2;
864     }
865     break;
866 sysadm 1.4 case CHANGE_NAME_DISPLAY:
867     display_nickname = !display_nickname;
868 sysadm 1.5 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
869 sysadm 1.1 {
870     log_error("section_list_draw_screen() error\n");
871     return -2;
872     }
873     break;
874 sysadm 1.20 case POST_ARTICLE:
875 sysadm 1.33 if (user_online_update("POST_ARTICLE") < 0)
876     {
877     log_error("user_online_update(POST_ARTICLE) error\n");
878     }
879    
880 sysadm 1.30 if ((ret = article_post(p_section, &article_new)) < 0)
881 sysadm 1.20 {
882 sysadm 1.26 log_error("article_post(sid=%d) error\n", p_section->sid);
883 sysadm 1.20 }
884 sysadm 1.24 else if (ret > 0) // New article posted
885     {
886 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
887 sysadm 1.24 if (ret < 0)
888     {
889     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
890     return -3;
891     }
892     }
893 sysadm 1.20 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
894     {
895     log_error("section_list_draw_screen() error\n");
896     return -2;
897     }
898     break;
899     case EDIT_ARTICLE:
900 sysadm 1.26 if (!checkpriv(&BBS_priv, p_section->sid, S_POST) ||
901     p_articles[selected_index]->uid != BBS_priv.uid)
902 sysadm 1.20 {
903 sysadm 1.26 break; // No permission
904 sysadm 1.20 }
905 sysadm 1.33
906     if (user_online_update("EDIT_ARTICLE") < 0)
907     {
908     log_error("user_online_update() error\n");
909     }
910    
911 sysadm 1.24 if (article_modify(p_section, p_articles[selected_index], &article_new) < 0)
912 sysadm 1.20 {
913 sysadm 1.26 log_error("article_modify(aid=%d) error\n", p_articles[selected_index]->aid);
914     }
915     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
916     {
917     log_error("section_list_draw_screen() error\n");
918     return -2;
919     }
920     break;
921     case DELETE_ARTICLE:
922     if (!checkpriv(&BBS_priv, p_section->sid, S_POST) ||
923     (!checkpriv(&BBS_priv, p_section->sid, S_MAN_S) && p_articles[selected_index]->uid != BBS_priv.uid))
924     {
925     break; // No permission
926     }
927 sysadm 1.30 if ((ret = article_del(p_section, p_articles[selected_index])) < 0)
928 sysadm 1.26 {
929     log_error("article_del(aid=%d) error\n", p_articles[selected_index]->aid);
930 sysadm 1.20 }
931 sysadm 1.30 else if (ret > 0) // Article deleted
932     {
933 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
934 sysadm 1.30 if (ret < 0)
935     {
936     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
937     return -3;
938     }
939     }
940 sysadm 1.20 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
941     {
942     log_error("section_list_draw_screen() error\n");
943     return -2;
944     }
945     break;
946 sysadm 1.41 case QUERY_ARTICLE:
947     if ((ret = display_article_meta(p_articles[selected_index]->aid)) < 0)
948     {
949     log_error("display_article_meta(aid=%d) error\n", p_articles[selected_index]->aid);
950     }
951     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
952     {
953     log_error("section_list_draw_screen() error\n");
954     return -2;
955     }
956     break;
957 sysadm 1.57 case QUERY_USER:
958 sysadm 1.61 if ((ret = query_user_info_by_uid(p_articles[selected_index]->uid, &user_info, user_intro, sizeof(user_intro))) < 0)
959 sysadm 1.57 {
960     log_error("query_user_info_by_uid(uid=%d) error\n", p_articles[selected_index]->uid);
961     return -2;
962     }
963     else if (ret == 0)
964     {
965     clearscr();
966     prints("该用户已升天");
967     press_any_key();
968     }
969     else if (user_info_display(&user_info) < 0) // && ret > 0
970     {
971     log_error("user_info_display(uid=%d) error\n", p_articles[selected_index]->uid);
972     }
973    
974     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
975     {
976     log_error("section_list_draw_screen() error\n");
977     return -2;
978     }
979     break;
980 sysadm 1.53 case SET_FAVOR_ARTICLE:
981     ret = article_favor_set(p_articles[selected_index]->tid == 0
982     ? p_articles[selected_index]->aid
983     : p_articles[selected_index]->tid,
984     &BBS_article_favor, 1);
985     if (ret < 0)
986     {
987     log_error("article_favor_set(aid=%d, 1) error\n",
988     p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
989     }
990     break;
991     case UNSET_FAVOR_ARTICLE:
992     ret = article_favor_set(p_articles[selected_index]->tid == 0
993     ? p_articles[selected_index]->aid
994     : p_articles[selected_index]->tid,
995     &BBS_article_favor, 0);
996     if (ret < 0)
997     {
998     log_error("article_favor_set(aid=%d, 0) error\n",
999     p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
1000     }
1001     break;
1002 sysadm 1.31 case FIRST_TOPIC_ARTICLE:
1003     case LAST_TOPIC_ARTICLE:
1004     page_id_cur = page_id;
1005     direction = (ret == FIRST_TOPIC_ARTICLE ? -1 : 1);
1006     ret = locate_article_in_section(p_section, p_articles[selected_index], direction, BBS_article_limit_per_section,
1007     &page_id, &selected_index, &article_count);
1008     if (ret < 0)
1009     {
1010     log_error("locate_article_in_section(sid=%d, aid=%d, direction=%d, step=%d) error\n",
1011     p_section->sid, p_articles[selected_index]->aid, direction, BBS_article_limit_per_section);
1012     return -3;
1013     }
1014     else if (ret > 0 && page_id != page_id_cur) // found and page changed
1015     {
1016 sysadm 1.36 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
1017 sysadm 1.31 if (ret < 0)
1018     {
1019     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
1020     return -3;
1021     }
1022     }
1023     break;
1024 sysadm 1.45 case SCAN_NEW_ARTICLE:
1025 sysadm 1.46 ret = scan_unread_article_in_section(p_section, p_articles[selected_index], &p_article_locate);
1026 sysadm 1.45 if (ret < 0)
1027     {
1028     log_error("scan_unread_article_in_section(sid=%d, aid=%d) error\n",
1029     p_section->sid, p_articles[selected_index]->aid);
1030     return -3;
1031     }
1032     else if (ret == 0) // not found
1033     {
1034     break;
1035     }
1036     page_id_cur = page_id;
1037 sysadm 1.46 ret = locate_article_in_section(p_section, p_article_locate, 0, 0,
1038 sysadm 1.45 &page_id, &selected_index, &article_count);
1039     if (ret < 0)
1040     {
1041     log_error("locate_article_in_section(sid=%d, aid=%d, direction=0, step=0) error\n",
1042 sysadm 1.46 p_section->sid, p_article_locate->aid);
1043 sysadm 1.45 return -3;
1044     }
1045     else if (ret > 0 && page_id != page_id_cur) // found and page changed
1046     {
1047     ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
1048     if (ret < 0)
1049     {
1050     log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
1051     return -3;
1052     }
1053     }
1054     break;
1055 sysadm 1.63 case SEARCH_USER:
1056     user_list_search();
1057     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1058     {
1059     log_error("section_list_draw_screen() error\n");
1060     return -2;
1061     }
1062     break;
1063 sysadm 1.28 case SHOW_HELP:
1064     // Display help information
1065     display_file(DATA_READ_HELP, 1);
1066     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1067     {
1068     log_error("section_list_draw_screen() error\n");
1069     return -2;
1070     }
1071     break;
1072 sysadm 1.37 case VIEW_EX_DIR:
1073     if (section_list_ex_dir_display(p_section) < 0)
1074     {
1075     log_error("section_list_ex_dir_display(sid=%d) error\n", p_section->sid);
1076     }
1077     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1078     {
1079     log_error("section_list_draw_screen() error\n");
1080     return -2;
1081     }
1082     break;
1083 sysadm 1.48 case SHOW_TOP10:
1084     show_top10_menu(NULL);
1085     if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1086     {
1087     log_error("section_list_draw_screen() error\n");
1088     return -2;
1089     }
1090     break;
1091 sysadm 1.1 default:
1092     log_error("Unknown command %d\n", ret);
1093     }
1094     }
1095    
1096     return 0;
1097     }
1098 sysadm 1.37
1099     int section_list_ex_dir_display(SECTION_LIST *p_section)
1100     {
1101     MENU_SET ex_menu_set;
1102     int ch = 0;
1103    
1104     if (p_section == NULL)
1105     {
1106     log_error("NULL pointer error\n");
1107     return -1;
1108     }
1109    
1110 sysadm 1.39 if (p_section->ex_menu_tm == 0) // N/A
1111     {
1112     moveto(2, 1);
1113     clrtoeol();
1114 sysadm 1.40 prints("该版块精华区未开放");
1115 sysadm 1.39 press_any_key();
1116     return 0;
1117     }
1118    
1119 sysadm 1.37 if (get_section_ex_menu_set(p_section, &ex_menu_set) < 0)
1120     {
1121     log_error("get_section_ex_menu_set(sid=%d) error\n", p_section->sid);
1122     return -3;
1123     }
1124     if (get_menu_shm_readonly(&ex_menu_set) < 0)
1125     {
1126 sysadm 1.39 log_error("get_menu_shm_readonly(sid=%d) error\n", p_section->sid);
1127 sysadm 1.37 return -3;
1128     }
1129    
1130     clearscr();
1131 sysadm 1.38 show_bottom("");
1132 sysadm 1.37
1133     if (display_menu(&ex_menu_set) == 0)
1134     {
1135     while (!SYS_server_exit)
1136     {
1137     iflush();
1138     ch = igetch(100);
1139 sysadm 1.54
1140     if (ch != KEY_NULL && ch != KEY_TIMEOUT)
1141     {
1142     BBS_last_access_tm = time(NULL);
1143     }
1144    
1145 sysadm 1.37 switch (ch)
1146     {
1147     case KEY_NULL: // broken pipe
1148 sysadm 1.54 log_error("KEY_NULL\n");
1149 sysadm 1.37 return 0;
1150     case KEY_TIMEOUT:
1151     if (time(NULL) - BBS_last_access_tm >= MAX_DELAY_TIME)
1152     {
1153 sysadm 1.54 log_error("User input timeout\n");
1154 sysadm 1.37 return 0;
1155     }
1156     continue;
1157     case CR:
1158     default:
1159     switch (menu_control(&ex_menu_set, ch))
1160     {
1161     case EXITMENU:
1162     ch = EXITMENU;
1163     break;
1164     case REDRAW:
1165     clearscr();
1166 sysadm 1.38 show_bottom("");
1167 sysadm 1.37 display_menu(&ex_menu_set);
1168     break;
1169     case NOREDRAW:
1170     case UNKNOWN_CMD:
1171     default:
1172     break;
1173     }
1174     }
1175    
1176     if (ch == EXITMENU)
1177     {
1178     break;
1179     }
1180     }
1181     }
1182    
1183     detach_menu_shm(&ex_menu_set);
1184    
1185     return 0;
1186     }
1187 sysadm 1.59
1188     int section_aid_locations_save(int uid)
1189     {
1190     char filename[FILE_PATH_LEN];
1191     FILE *fp;
1192     int i;
1193     int ret = 0;
1194    
1195     snprintf(filename, sizeof(filename), "%s/%d", VAR_SECTION_AID_LOC_DIR, uid);
1196    
1197     if ((fp = fopen(filename, "wb")) == NULL)
1198     {
1199     log_error("fopen(%s, wb) error: %d\n", filename, errno);
1200     return -1;
1201     }
1202    
1203     for (i = 0; i < p_section_list_pool->section_count; i++)
1204     {
1205     if (fwrite(&(p_section_list_pool->sections[i].sid), sizeof(p_section_list_pool->sections[i].sid), 1, fp) != 1)
1206     {
1207     log_error("fwrite(%s, sid) error\n", filename);
1208     ret = -2;
1209     break;
1210     }
1211    
1212     if (fwrite(&(section_aid_locations[i]), sizeof(section_aid_locations[i]), 1, fp) != 1)
1213     {
1214     log_error("fwrite(%s, aid) error\n", filename);
1215     ret = -2;
1216     break;
1217     }
1218     }
1219    
1220     if (fclose(fp) < 0)
1221     {
1222     log_error("fclose(%s) error: %d\n", filename, errno);
1223     ret = -1;
1224     }
1225    
1226     return ret;
1227     }
1228    
1229     int section_aid_locations_load(int uid)
1230     {
1231     char filename[FILE_PATH_LEN];
1232     FILE *fp;
1233     int i;
1234     int32_t sid;
1235     int32_t aid;
1236     SECTION_LIST *p_section;
1237     int ret = 0;
1238    
1239     snprintf(filename, sizeof(filename), "%s/%d", VAR_SECTION_AID_LOC_DIR, uid);
1240    
1241     if ((fp = fopen(filename, "rb")) == NULL)
1242     {
1243     if (errno == ENOENT) // file not exist
1244     {
1245     return 0;
1246     }
1247     log_error("fopen(%s, rb) error: %d\n", filename, errno);
1248     return -1;
1249     }
1250    
1251     while (!feof(fp))
1252     {
1253     if (fread(&sid, sizeof(sid), 1, fp) != 1)
1254     {
1255     if (ferror(fp) == 0)
1256     {
1257     break;
1258     }
1259     log_error("fread(%s, sid) error: %d\n", filename, ferror(fp));
1260     ret = -2;
1261     break;
1262     }
1263    
1264     if (fread(&aid, sizeof(aid), 1, fp) != 1)
1265     {
1266     if (ferror(fp) == 0)
1267     {
1268     break;
1269     }
1270     log_error("fread(%s, aid) error: %d\n", filename, ferror(fp));
1271     ret = -2;
1272     break;
1273     }
1274    
1275     p_section = section_list_find_by_sid(sid);
1276     if (p_section == NULL)
1277     {
1278     continue; // skip section no longer exist
1279     }
1280    
1281     i = get_section_index(p_section);
1282     if (i < 0)
1283     {
1284     log_error("get_section_index(sid=%d) error\n", sid);
1285     ret = -3;
1286     break;
1287     }
1288     section_aid_locations[i] = aid;
1289     }
1290    
1291     if (fclose(fp) < 0)
1292     {
1293     log_error("fclose(%s) error: %d\n", filename, errno);
1294     ret = -1;
1295     }
1296    
1297     return ret;
1298     }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1