티스토리 뷰
협업 개발 시스템인 Trac을 분석하기 위해서는 먼저 "협업 개발 시스템 Trac 수정을 위한 개발 환경 준비하기"를 통해 개발 환경을 준비해야 합니다.
프로그램 분석 과정에서 상당히 중요한 부분 중의 하나가 바로 프로그램의 시작점을 찾는 것입니다. Trac 프로젝트의 프로그램의 시작점은 크게 2가지로 나뉘는데 하나는 trac-admin이고 다른 하나는 웹 인터페이스를 관장하는 tracd 입니다. Trac의 핵심 기능은 웹서버나 tracd를 통해서 플러그인 형태로 제공되는데 관련 정보는 설치 코드 위치의 Trac.egg-info\entry_points.txt에 있으며 그 내용은 아래와 같습니다.
[console_scripts]
trac-admin = trac.admin.console:run
tracd = trac.web.standalone:main
[trac.plugins]
trac.about = trac.about
trac.admin.console = trac.admin.console
trac.admin.web_ui = trac.admin.web_ui
trac.attachment = trac.attachment
trac.db.mysql = trac.db.mysql_backend
trac.db.postgres = trac.db.postgres_backend
trac.db.sqlite = trac.db.sqlite_backend
trac.mimeview.patch = trac.mimeview.patch
trac.mimeview.pygments = trac.mimeview.pygments[Pygments]
trac.mimeview.rst = trac.mimeview.rst[reST]
trac.mimeview.txtl = trac.mimeview.txtl[Textile]
trac.prefs = trac.prefs.web_ui
trac.search = trac.search.web_ui
trac.ticket.admin = trac.ticket.admin
trac.ticket.batch = trac.ticket.batch
trac.ticket.query = trac.ticket.query
trac.ticket.report = trac.ticket.report
trac.ticket.roadmap = trac.ticket.roadmap
trac.ticket.web_ui = trac.ticket.web_ui
trac.timeline = trac.timeline.web_ui
trac.versioncontrol.admin = trac.versioncontrol.admin
trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz
trac.versioncontrol.web_ui = trac.versioncontrol.web_ui
trac.web.auth = trac.web.auth
trac.web.session = trac.web.session
trac.wiki.admin = trac.wiki.admin
trac.wiki.interwiki = trac.wiki.interwiki
trac.wiki.macros = trac.wiki.macros
trac.wiki.web_ui = trac.wiki.web_ui
trac.wiki.web_api = trac.wiki.web_api
tracopt.mimeview.enscript = tracopt.mimeview.enscript
tracopt.mimeview.php = tracopt.mimeview.php
tracopt.mimeview.silvercity = tracopt.mimeview.silvercity[SilverCity]
tracopt.perm.authz_policy = tracopt.perm.authz_policy
tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider
tracopt.ticket.clone = tracopt.ticket.clone
tracopt.ticket.commit_updater = tracopt.ticket.commit_updater
tracopt.ticket.deleter = tracopt.ticket.deleter
tracopt.versioncontrol.git.git_fs = tracopt.versioncontrol.git.git_fs
tracopt.versioncontrol.svn.svn_fs = tracopt.versioncontrol.svn.svn_fs
tracopt.versioncontrol.svn.svn_prop = tracopt.versioncontrol.svn.svn_prop
tracd의 시작점인 trac\web\standalone.py의 main() 함수는 전달된 옵션을 파싱하고 옵션에 따라 내장 웹서버를 가동 시키는 형태 입니다. 아래는 main()함수의 일부 입니다.
def serve(): addr, port = server_address if not addr or addr == '0.0.0.0': loc = '0.0.0.0:%s view at http://127.0.0.1:%s/%s' \ % (port, port, base_path) else: loc = 'http://%s:%s/%s' % (addr, port, base_path) try: httpd = TracHTTPServer(server_address, wsgi_app, options.env_parent_dir, args, use_http_11=options.http11) except socket.error, e: print 'Error starting Trac server on %s' % loc print '[Errno %s] %s' % e.args sys.exit(1) print 'Server starting in PID %i.' % os.getpid() print 'Serving on %s' % loc if options.http11: print 'Using HTTP/1.1 protocol version' httpd.serve_forever()
tracd의 종료 시점까지 웹으로 부터 전달되는 각종 요청은 trac\web\main.py의 dispatch_request()함수에 의해 처리 됩니다. dispatch_request()에서는 우선 "http://127.0.0.1:8000/devtest/wiki"와 같은 방식으로 전달된 URL을 분석하여 어떤 프로젝트에 접근 하려는지를 찾아(예제 URL에서는 devtest) 프로젝트 환경을 로드 합니다. 프로젝트 환경은 아래의 그림과 같이 프로젝트 단위의 폴더에 환경파일(trac.ini), 데이터베이스, 프로젝트 단위의 웹리소스(이미지, 문서등)와 플러그인 및 템플릿, 위키와 티켓의 첨부 파일로 구성 됩니다.
URL에 프로젝트를 지정하지 않은 경우에는 전체 프로젝트 목록 정보를 main.py에 있는 send_project_index() 함수를 통해서 출력 합니다. send_project_index()는 trac\templates 폴더에 env_index_template이 환경에 있다면 env_index_template을 템플릿으로 사용하고 없다면 템플릿 폴더의 index.html을(기본값) 템플릿으로 사용 합니다.
프로젝트 환경을 로드한 다음 _dispatch_request()함수와 RequestDispatcher 클래스의 dispatch()를 통해서 웹 요청에 대한 적절한 핸들러를 할당하고 사용자 인증과 핸들러 전/후처리를 수행 합니다.
dispatch()에서는 IRequestHandler를 적용한 핸들러들을 URL 기준으로 검색하여 해당 웹 요청을 처리할 핸들러를 선택 합니다. IRequestHandler를 적용한 핸들러 모듈은 아래의 코드 예제 처럼 match_request 메쏘드를 통해서 자신이 처리할 URL 형태이면 True를 리턴 합니다.
class AboutModule(Component): """"About Trac" page provider, showing version information from third-party packages, as well as configuration information.""" required = True implements(INavigationContributor, IPermissionRequestor, IRequestHandler) # INavigationContributor methods def get_active_navigation_item(self, req): return 'about' def get_navigation_items(self, req): yield ('metanav', 'about', tag.a(_('About Trac'), href=req.href.about())) # IPermissionRequestor methods def get_permission_actions(self): return ['CONFIG_VIEW'] # IRequestHandler methods def match_request(self, req): return re.match(r'/about(?:_trac)?(?:/.+)?$', req.path_info) def process_request(self, req): data = {'systeminfo': None, 'plugins': None, 'config': None} ......
- AboutModule(trac/about.py) : Trac 소개 페이지 출력
"/about" 또는 "/about_trac" CONFIG_VIEW 권한 - AttachmentModule(trac/attachment.py) : 첨부 파일 처리
"/attachment/ticket/티켓번호" 또는 "/attachment/wiki/위키이름", "/attachment" 대신 "raw-attachment"이 사용될 수 있고 "zip-attachment"로 요청되면 첨부를 압축해서 내려받을 수 있도록 합니다. - AdminModule(trac/admin/web_ui.py) : 관리자 페이지
"/admin" TRAC_ADMIN 권한 - PygmentsRenderer(trac/mimeview/pygments.py): 파이썬 문법 표시기(http://pygments.org/)
"/pygments/*.css" - PreferencesModule(trac/prefs/web_ui.py) : 개인 설정 관리
"/prefs" - SearchModule(trac/search/web_ui.py) : 검색
"/search", "/search/opensearch" SEARCH_VIEW 권한 - BatchModifyModule(trac/ticket/batch.py) : 티켓 일괄 변경
"/batchmodify" TICKET_BATCH_MODIFY 권한 - QueryModule(trac/ticket/query.py) : 티켓 검색
"/query" TICKET_VIEW 권한 - ReportModule(trac/ticket/report.py) : 티켓 보고서
"/report/" REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE, REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW 권한 - RoadmapModule(trac/ticket/roadmap.py) : 마일스톤 대시보드
"/roadmap" ROADMAP_ADMIN, MILESTONE_CREATE, MILESTONE_DELETE, MILESTONE_MODIFY, MILESTONE_VIEW, ROADMAP_VIEW 권한 - MilestoneModule(trac/ticket/roadmap.py) : 마일스톤 조회 및 편집
"/milestone" MILESTONE_ADMIN, MILESTONE_CREATE, MILESTONE_DELETE, MILESTONE_MODIFY, MILESTONE_VIEW 권한 - TicketModule(trac/ticket/web_ui.py) : 티켓 추가 및 편집
"/newticket", "/ticket/티켓번호" TICKET_VIEW, TICKET_CREATE, TICKET_EDIT_COMMENT, TICKET_CHGPROP, TICKET_APPEND, TICKET_MODIFY 권한 - TimelineModule(trac/timeline/web_ui.py) : 프로젝트 히스토리
"/timeline" TIMELINE_VIEW 권한 - LoginModule(trac/web/auth.py) : 로그인, 로그아웃
"/login", "/logout" - Chrome(trac/web/chrome.py) : 웹 관리자
"/chrome/site/파일명" 프로젝트 환경의 htdocs폴더, "/chrome/common/파일명" Trac코드/trac/htdocs폴더, "/chrome/shared/파일명" 프로젝트간 공유 폴더(trac.init>[inherit]>htdocs_dir로 지정) - InterTracDispatcher(trac/wiki/intertrac.py) : Trac간 연동
"/intertrac/경로" - WikiRenderer(trac/wiki/web_api.py) : 위키 텍스트 표시기
"/wiki_render" - WikiModule(trac/wiki/web_ui.py) : 위키 모듈
"/wiki/" WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, WIKI_RENAME, WIKI_VIEW - BrowserModule(trac/versioncontrol/web_ui/browser.py) : 서브버전 브라우저
"/export/파일명", "/browser/파일명", "/file/파일명" BROWSER_VIEW, FILE_VIEW 권한 - ChangesetModule(trac/versioncontrol/web_ui/changeset.py) : 서브버전 변동사항
"/changeset/" CHANGESET_VIEW 권한 - LogModule(trac/versioncontrol/web_ui/log.py) : 서브버전 로그
"/log/" LOG_VIEW 권한
def process_request(self, req): data = {'systeminfo': None, 'plugins': None, 'config': None} if 'CONFIG_VIEW' in req.perm('config', 'systeminfo'): # Collect system information data['systeminfo'] = self.env.get_systeminfo() if 'CONFIG_VIEW' in req.perm('config', 'plugins'): # Collect plugin information data['plugins'] = get_plugin_info(self.env) if 'CONFIG_VIEW' in req.perm('config', 'ini'): # Collect config information defaults = self.config.defaults(self.compmgr) sections = [] for section in self.config.sections(self.compmgr): options = [] default_options = defaults.get(section, {}) for name, value in self.config.options(section, self.compmgr): default = default_options.get(name) or '' options.append({ 'name': name, 'value': value, 'modified': unicode(value) != unicode(default) }) options.sort(key=lambda o: o['name']) sections.append({'name': section, 'options': options}) sections.sort(key=lambda s: s['name']) data['config'] = sections return 'about.html', data, None
■ Trac의 템플릿
웹 요청을 처리하는 핸들러 메쏘드 process_request()의 결과는 필요한 템플릿과 데이터로 아래는 Trac에서 사용되고 있는 템플릿의 구조를 간단하게 나타낸 것입니다.
* trac * templates 기본 템플릿 about.html attachment.html attach_file_form.html author_or_creator.rss diff_div.html diff_options.html diff_view.html error.html history_view.html index.html layout.html list_of_attachments.html macros.html macros.rss page_index.html preview_file.html progress_bar.html progress_bar_grouped.html theme.html * admin templates 관리자 템플릿 admin.html admin_basics.html admin_components.html admin_enums.html admin_legacy.html admin_logging.html admin_milestones.html admin_perms.html admin_plugins.html admin_versions.html deploy_trac.cgi deploy_trac.fcgi deploy_trac.wsgi * prefs templates 설정 템플릿 prefs.html prefs_advanced.html prefs_datetime.html prefs_general.html prefs_keybindings.html prefs_language.html prefs_pygments.html prefs_userinterface.html * search templates 검색 템플릿 opensearch.xml search.html * ticket templates 티켓 템플릿 batch_modify.html batch_ticket_notify_email.txt milestone_delete.html milestone_edit.html milestone_view.html query.html query.rss query_results.html report.rss report_delete.html report_edit.html report_list.html report_list.rss report_view.html roadmap.html ticket.html ticket.rss ticket_box.html ticket_change.html ticket_notify_email.txt ticket_preview.html * timeline templates 프로젝트 기록 템플릿 timeline.html timeline.rss * versioncontrol templates 형상관리 템플릿 admin_repositories.html browser.html changeset.html diff_form.html dirlist_thead.html dir_entries.html path_links.html repository_index.html revisionlog.html revisionlog.rss revisionlog.txt sortable_th.html * wiki templates 기본 템플릿 wiki_delete.html wiki_diff.html wiki_edit.html wiki_edit_form.html wiki_page_path.html wiki_rename.html wiki_view.html
■ Trac 데이터베이스
아래는 Trac 데이터베이스의 테이블 상세 내용 입니다. 여러 프로젝트를 별도의 Trac으로 관리하고 싶다면 프로젝트 단위로 데이터베이스를 생성해야 합니다.
========== attachment ========== +-------------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------+------+-----+---------+-------+ | type | text | NO | PRI | NULL | | | id | text | NO | PRI | NULL | | | filename | text | NO | PRI | NULL | | | size | int(11) | YES | | NULL | | | time | bigint(20) | YES | | NULL | | | description | text | YES | | NULL | | | author | text | YES | | NULL | | | ipnr | text | YES | | NULL | | +-------------+------------+------+-----+---------+-------+ ========== auth_cookie ========== +--------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+---------+------+-----+---------+-------+ | cookie | text | NO | PRI | NULL | | | name | text | NO | PRI | NULL | | | ipnr | text | NO | PRI | NULL | | | time | int(11) | YES | | NULL | | +--------+---------+------+-----+---------+-------+ ========== cache ========== +------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+---------+------+-----+---------+-------+ | id | int(11) | NO | PRI | 0 | | | generation | int(11) | YES | | NULL | | | key | text | YES | | NULL | | +------------+---------+------+-----+---------+-------+ ========== component ========== +-------------+------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+------+------+-----+---------+-------+ | name | text | NO | PRI | NULL | | | owner | text | YES | | NULL | | | description | text | YES | | NULL | | +-------------+------+------+-----+---------+-------+ ========== enum ========== +-------+------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------+------+-----+---------+-------+ | type | text | NO | PRI | NULL | | | name | text | NO | PRI | NULL | | | value | text | YES | | NULL | | +-------+------+------+-----+---------+-------+ ========== milestone ========== +-------------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------+------+-----+---------+-------+ | name | text | NO | PRI | NULL | | | due | bigint(20) | YES | | NULL | | | completed | bigint(20) | YES | | NULL | | | description | text | YES | | NULL | | +-------------+------------+------+-----+---------+-------+ ========== node_change ========== +-------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------+------+-----+---------+-------+ | repos | int(11) | NO | PRI | 0 | | | rev | text | NO | PRI | NULL | | | path | text | NO | PRI | NULL | | | node_type | text | YES | | NULL | | | change_type | text | NO | PRI | NULL | | | base_path | text | YES | | NULL | | | base_rev | text | YES | | NULL | | +-------------+---------+------+-----+---------+-------+ ========== permission ========== +----------+------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+------+------+-----+---------+-------+ | username | text | NO | PRI | NULL | | | action | text | NO | PRI | NULL | | +----------+------+------+-----+---------+-------+ ========== report ========== +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | author | text | YES | | NULL | | | title | text | YES | | NULL | | | query | text | YES | | NULL | | | description | text | YES | | NULL | | +-------------+------------------+------+-----+---------+----------------+ ========== repository ========== +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | id | int(11) | NO | PRI | 0 | | | name | text | NO | PRI | NULL | | | value | text | YES | | NULL | | +-------+---------+------+-----+---------+-------+ ========== revision ========== +---------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+------------+------+-----+---------+-------+ | repos | int(11) | NO | PRI | 0 | | | rev | text | NO | PRI | NULL | | | time | bigint(20) | YES | | NULL | | | author | text | YES | | NULL | | | message | text | YES | | NULL | | +---------+------------+------+-----+---------+-------+ ========== session ========== +---------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+---------+------+-----+---------+-------+ | sid | text | NO | PRI | NULL | | | authenticated | int(11) | NO | PRI | 0 | | | last_visit | int(11) | YES | MUL | NULL | | +---------------+---------+------+-----+---------+-------+ ========== session_attribute ========== +---------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+---------+------+-----+---------+-------+ | sid | text | NO | PRI | NULL | | | authenticated | int(11) | NO | PRI | 0 | | | name | text | NO | PRI | NULL | | | value | text | YES | | NULL | | +---------------+---------+------+-----+---------+-------+ ========== system ========== +-------+------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------+------+-----+---------+-------+ | name | text | NO | PRI | NULL | | | value | text | YES | | NULL | | +-------+------+------+-----+---------+-------+ ========== ticket ========== +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | type | text | YES | | NULL | | | time | bigint(20) | YES | MUL | NULL | | | changetime | bigint(20) | YES | | NULL | | | component | text | YES | | NULL | | | severity | text | YES | | NULL | | | priority | text | YES | | NULL | | | owner | text | YES | | NULL | | | reporter | text | YES | | NULL | | | cc | text | YES | | NULL | | | version | text | YES | | NULL | | | milestone | text | YES | | NULL | | | status | text | YES | MUL | NULL | | | resolution | text | YES | | NULL | | | summary | text | YES | | NULL | | | description | text | YES | | NULL | | | keywords | text | YES | | NULL | | +-------------+------------------+------+-----+---------+----------------+ ========== ticket_change ========== +----------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+------------+------+-----+---------+-------+ | ticket | int(11) | NO | PRI | 0 | | | time | bigint(20) | NO | PRI | 0 | | | author | text | YES | | NULL | | | field | text | NO | PRI | NULL | | | oldvalue | text | YES | | NULL | | | newvalue | text | YES | | NULL | | +----------+------------+------+-----+---------+-------+ ========== ticket_custom ========== +--------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+---------+------+-----+---------+-------+ | ticket | int(11) | NO | PRI | 0 | | | name | text | NO | PRI | NULL | | | value | text | YES | | NULL | | +--------+---------+------+-----+---------+-------+ ========== version ========== +-------------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------+------+-----+---------+-------+ | name | text | NO | PRI | NULL | | | time | bigint(20) | YES | | NULL | | | description | text | YES | | NULL | | +-------------+------------+------+-----+---------+-------+ ========== wiki ========== +----------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+------------+------+-----+---------+-------+ | name | text | NO | PRI | NULL | | | version | int(11) | NO | PRI | 0 | | | time | bigint(20) | YES | MUL | NULL | | | author | text | YES | | NULL | | | ipnr | text | YES | | NULL | | | text | text | YES | | NULL | | | comment | text | YES | | NULL | | | readonly | int(11) | YES | | NULL | | +----------+------------+------+-----+---------+-------+
'IT 일반' 카테고리의 다른 글
간단한 MySQL 설치 요령 (0) | 2019.01.01 |
---|---|
이슈 트래커와 형상 관리 도구를 사용하기 위한 일반적 지식 (0) | 2018.12.29 |
웹 출력을 위한 파이썬 툴킷 젠시(Genshi) (0) | 2018.12.27 |
협업 개발 시스템 Trac 수정을 위한 개발 환경 준비하기 (0) | 2018.12.27 |
CPU의 이해와 구성 - 정보처리 필기 해설 9 (0) | 2017.04.12 |